Reputation: 91
As i was going through Null saftey in Kotlin, here in hyperskill THEORY it was mentioned that Kotlin has a real remedy for NPE.
Consider below code (1) from Oliver Charlesworth
class Foo {
val c: String // Non-nullable
init {
bar()
c = "" // Initialised for the first time here
}
fun bar() {
println(c.length) // Oh dear
}
}
fun main(args: Array<String>) {
Foo()
}
Now above will give NPE. To avoid it, Kotlin uses '?'
code(2)
val c:String? = null
fun bar() {
print(c?.length)
}
Also instead of that i could have used like,
fun bar() {
if(c != null){
print(c.length)
}
}
Question
1.If i could just use if statement than why would i use code (2) ?
2.Is it because of code (2)'s conciseness ? or there is more to it that i am missing ?
Upvotes: 0
Views: 55
Reputation: 93834
You are confusing two different things. The ?.
operator isn't designed to fix the problem illustrated in your first code block. It's designed for nullable properties and variables, not for non-nullable ones like in your first code block.
Set aside your your question about code(1) for a moment because that is secondary to understanding null-safety as you describe in your question about code(2).
Kotlin is designed where nullability is almost* always predictable. I'll get back to the "almost".
If you define a property, variable, or parameter as nullable, then the language forces you to treat it in a way that won't allow you to trigger a NullPointerException (caused by accessing its member functions or properties when its actually null).
If you define a property, variable, or parameter as non-nullable, then you don't have to do null checks. There's no need for using ?.
with a non-nullable because you already know it's not null.
So now, addressing your question about code(2): The use of ?.
is different from the if
check for at least a couple reasons:
It's more concise. This is especially nice if you want to access a member of a member (of a member, and so on) of your nullable property, like in @broot's answer's example.
It can handle properties that might be changing from another thread as your code is running. Suppose while you're doing a null check on a property, another thread changes it to null in between when you determined it was not null and when you tried to use it. The ?.
call will be safe, but the if check would throw a NullPointerException. Therefore, the Kotlin compiler won't even let you do it the if-check way if it's possible for a property to be modified from some other thread.
They are not exactly the same, and there are cases where an if
is more appropriate. Your two pieces of example code are NOT equivalent because the first one always prints something--it just prints null
if the variable was null. The second one with the if
check only prints the String if it wasn't null. This might be the behavior you want in some situations.
Finally, getting back to the "almost", there are a couple of situations involving constructors/init blocks where you can cause your non-nullable properties to be accessed before they are initialized and therefore break Kotlin's null-safety features. Your example in code(1) is one example.
The ?.
operator isn't designed for handling these kinds of situations, because you aren't supposed to be doing null-checks for non-nullables.
Instead, you shouldn't be creating these situations in the first place. Be extremely careful about calling functions from init
blocks and property initializers if those functions use properties from the same class. Prefer initializing properties on their declaration line instead of in init
blocks. And never call an open
function from an init
block or property initializer.
If you follow these rules, you won't have the null inconsistency problem.
Upvotes: 1
Reputation: 28432
I'm not sure if I fully understand your question. Object initialization is one of the very few cases where null safety in Kotlin is partially disabled. This is mentioned in the documentation about the null safety:
The only possible causes of an NPE in Kotlin are: (...) Data inconsistency with regard to initialization
Possible explanations are: technical limitations imposed by the underlying Java; or intentional decision, because guaranteed null-safety during the initialization might be pretty annoying for the developer. No matter the reason, developers are expected to be careful when implementing the initialization logic or better, at all avoid complex logic there.
Answering your question on which code sample is better. None of them. Ideally, we should use simply println(c.length)
, but make sure it never executes before initializing c
.
Update
If your concerns are not at all related to object initialization, but only about the if
versus ?.
. First, your code samples aren't the same. First code sample always invokes print()
, potentially passing null
to it. Second code sample doesn't invoke print()
in the case of null.
But general answer is: yes, this is for conciseness. In Java we often have to write code like this (let's ignore Law of Demeter):
if (foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.qux != null)
In Kotlin this is simply:
if (foo?.bar?.baz?.qux != null)
Upvotes: 2