Home > Software design >  How to deal with non initialized lateinit vars
How to deal with non initialized lateinit vars

Time:08-20

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.

  • Related