What is the usage for the "?" character?

Originally asked by Paul Fieber on 06 April 2020 (original question)


I see inconsistent use of the “?” character. In some examples it is present, but in other examples is it not.

Outgoing rule:
replica.customFields.“Custom field name” = issue.customFields.“Local custom field name”

Incoming rule:
issue.customFields.“Local custom field name”.value = replica.customFields.“Custom field name”.value

vs

issue.customFields​.“Labels”.value + = replica.customFields.“label - cust1”?.value

In the first set of rules, there is no “?” anywhere, but in the second on there is a single “?” in the replica, yet no matching “?” in the issue side. In other examples I have seen it on both ends of the equation, but others, apparently identical in construction, do not have it at all. I have no idea what it actually does or why I should use/not use it.

When should I use the “?” or not? Is there a scripting syntax guide that explains this somewhere? I have looked for one but not found it yet.


Answer by Hung Nguyen on 06 April 2020

Francis has provided all the details you need.

But to answer your question to the point, I’d like to leave some of my understanding here:

? used before a dot (.) operator to make it a “safer dot” operator.

Because if the object before the dot is Null, then an expression like this:

object.name will throw exception NullPointerException

while

object?.name will just return Null, without throwing exception, hence safer for the case you want to say: If object is NOT null, then return its name, else return null

You would NOT want use this operator on the left side of an assignment of course, because if it is, there will be case where the left side become Null and you will assign something to a null space.

Hope this clarifies your questions.


Answer by Francis Martens (Exalate) on 06 April 2020

The ‘?’ operator is called the ‘Save Navigation’ operator - a groovy construct allowing to avoid nullpointer exceptions

for instance

// generates a NullPointerException
def foo = null
shouldFail(NullPointerException) {
    def bar = foo.value
}


while

// avoid the null pointer exception
def foo = null
assert(foo?.value, null)

More information on this can be found here

Also interesting is the Elvis Operator ?:

It allows to construct items like

// typeName becomes Task if replica.typeName == null
issue.typeName = replica.typeName ?: "Task"

PS. The snippets on the documentation site are snippets. Due are indeed due for a review.