I'm migrating a project from java to kotlin, and that project used a lot of variables that can be null and they are not initialized until some interaction with the user or external jobs have been done.
I'm trying to use kotlin null safety advantages, and I'm trying to avoid the use of null
in the source code, so i'm dealing with all the variables using lateinit
, like this: lateinit var location: Location
instead of using var location: Location? = null
I'm doing it to avoid using ?
each time I need to use those variables. The issue now is... what happens if the variables has been not initialized?
For example, what happens if I need to call location.getX()
in some part of the code? How to deal with cases in which location is still not initialized?
I figured out to deal with it using this:
if (!this::location.isInitialized){
location.getX()
}
But then, I'm filling the code with that boilerplate code of isInitialized etc... so... at the end, I prefeer the old java version of the project without null safety, it's more clear and short.
If I come back to var location: Location? = null
, then, I need to use
location.let {
it.getX()
}
or
location?.getX()
and in this second case I'm still dealing with null, that is something I was trying to avoid
Is something I'm doing wrong? must I come back to null variables and var location: Location? = null
instead of using lateinit
? Exists a better approach to do this?
CodePudding user response:
I think you have a wrong concept of how to use null in Kotlin. It is precisely because of Kotlin’s null safety features that you can safely make properties nullable. If something is not available at all times, it should be nullable. You are misusing lateinit
in a way that makes your properties unsafe by forcing you to remember to check if they are initialized, the same way you always have to remember to check if fields are null in Java. So you have re-introduced the same problem that Kotlin null-safety solved.
If something is sometimes not available, it is perfectly appropriate to make it nullable. Kotlin’s null-safety features will ensure you don’t forget to check this possible state.
More reading here, from the Kotlin design lead: Null is your friend
lateinit
should only ever be used by properties that can never be null after onCreate()
.
——
Maybe what you’re asking is how to more concisely check null on the Location without having to deal with its inner properties still being nullable? Aside from the ?.let
pattern, you can use this:
val location = location ?: return
// you can now freely use non-null location in this function
// from the local reference known to be non-null
The ?.let
case is much like the if (x != null)
pattern in Java, except that it is also safe for properties that might be changed in multiple threads.
location?.let { location ->
// freely use location.getX() and getY() inside the braces
}
Between these two patterns, you always have code that is just as concise as the Java way of checking it.
CodePudding user response:
lateinit
should only ever be used for cases when they variable is guaranteed to be initialized, it's just not possible to verify this guarantee at compile time. A good example is Activity.onCreate
: while the Kotlin compiler has no way of knowing that a field initialized in onCreate
is guaranteed to not be null in onResume
, it's a pretty strong contract in the Android SDK, so it's reasonable to use lateinit
for that field and avoid null-checks.
For anything that's not guaranteed to be non-null at access time, stick to nullable types and do null-checks as usual. For anything that's guaranteed to be non-null, use lateinit
without any extra checks: if those call-sites lead to crashes, remove lateinit
and change the type to nullable.
CodePudding user response:
you can simplify x?.let {}
change:
location.let {
it.getX()
}
to:
location?.getX()
And set a default value like:
val x = location?.getX() ?: 0
CodePudding user response:
I'm migrating a project from java to kotlin, and that project used a lot of variables that can be null and they are not initialized until some interaction with the user or external jobs have been done.
You can use lazy initialization for properties that need to wait for interaction.
foo: MyType by lazy { ... }
There is a post about that Property initialization using "by lazy" vs. "lateinit"
As Egor pointed out, lateinit should be used when you can warrant that a property will be initialized. Still you can check for initialization with:
this::myLateinitVar.isInitialized
But if your source code consists about nullables, then you should definitively back to nullable types in Kotlin and make use of the ? operator. After all, it's a tool there for you to use.