Why doesn't Kotlin allow you to use lateinit with primitive types?
Asked Answered
I

3

75

In the Kotlin language we, by default, have to initialize each variable when it is introduced. To avoid this, the lateinit keyword can be used. Referring to a lateinit variable before it has been initialized results in a runtime exception.

lateinit can not, however, be used with the primitive types. Why is it so?

Inhibit answered 4/8, 2016 at 7:40 Comment(2)
It would be helpful if the question could be clarified, though it may be too late for that. Usually when people say "primitive types", they include only those like Java int, not the boxed version like Java Integer. But in Kotlin when you try to declare lateinit var foo: Int, the error message says 'lateinit' modifier is not allowed on properties of primitive types, implying that Int is a primitive type, even though Kotlin is supposed to be able to use either int or Integer under the hood as needed. I think this inconsistency (or ambiguity, at best) adds a lot to the confusion.Biome
So in other words, sometimes answers are explaining why you can't make an int variable lateinit, because it's a primitive type in the strict sense; but this doesn't tell us why you can't make an Int variable lateinit (since Int is not always implemented as a primitive type under the hood).Biome
C
68

For (non-nullable) object types, Kotlin uses the null value to mark that a lateinit property has not been initialized and to throw the appropriate exception when the property is accessed.

For primitive types, there is no such value, so there is no way to mark a property as non-initialized and to provide the diagnostics that lateinit needs to provide. (We could try to use a separate marker of some kind, but that marker would not be updated when initializing the field through reflection, which is a major use case of lateinit).

Therefore, lateinit is supported for properties of object types only.

Cobbler answered 4/8, 2016 at 13:46 Comment(13)
Could you clarify in the answer, why lateinit Int couldn't be represented with the Integer type in runtime, which is able to hold null value while it hasn't been initialized?Skit
@Skit because an Integer is not an int. It is a boxed type, and it matters when setting stuff natively or through reflection.Temperate
So, I think Kotlin wraps up the primitive types to object(for example int to Int). However, why can't Int hold null as it is an object ? Also, why can't we declare a parameter as nullable and lateinit it ? (var x: Int?)Alis
@Alis for the same reason explained by yole. Basically, you need an unused value that can be used to flag the fact that the var is not inited. As explained, there is no such value for primitives (all values that can be hold by a primitive var are valid). The same problem holds for nullable vars: all values, included null, are valid (you can init a nullable var with null). So no unused value remains to mark the var as not inited.Pronominal
@GustavoMaciel but if I say lateinit var int: Int? then I don't see why that wouldn't work, since Kotlin is already representing Int? with Integer ...Funeral
@Funeral Because lateinit and ? are conflicting. The former says you have a variable that will always have some value after being initialized, the latter says you have a variable that may or may not have some value. You shouldn't need to ?. or ever check for nullity on a lateinit var.Temperate
@GustavoMaciel that makes senseFuneral
@GustavoMaciel sometimes, you need to distinguish the variable initialized as a null and uninitialized variable.Trousseau
The difference between Int and Int? is, that the first can have all numbers as value, the second can have all numbers and null as value. So why can't we have a lateinit Int that has null as marker value for not yet initialized? :-/Through
So what is the recommendation for primitives, just keep them as var and initialize them to zero (even though it isn't a valid value)?Lunge
I was under the impression, that all this java-like primitives vs. objects thing is now gone... But now I cannot use lateinit on Double...Jorrie
I think that all that makes sense in a technical way (like so many restrictions and quirks do in Java). But nevertheless, it' just confusing from a developers standpoint. What ever the best workaround for this might be, it should IHMO be handled by the compiler.Jorrie
I understand why (from the standpoint of how Kotlin designers chose to implement lateinit) nullable types, such as Int?, can't be made lateinit. But I don't see why Kotlin can't compile lateinit var x: Int in a way that uses the boxed Integer object under the hood, as it would do with (non-lateinit) var x: Int?.Biome
B
9

A short answer is that with primitives you can always use 0 as the default, and with nullable types null as a default. Only non-nullable non-primitive types may need lateinit to work around the type safety system.

Actually, there is no need for initializing a variable in Kotlin as long as it has a value before the first access and it can be statically proved. Which means this code is perfectly valid:

fun main(args: Array<String>) {
    var x: Int
    val y: Double

    x = 0
    y = x + 0.1

    println("$x, $y") 
}

But there are (rare) cases when the initialisation cannot be statically proved. The most common case is a class field which uses any form of dependency injection:

class Window {
    @Inject lateinit parent: Parent
}
Brute answered 4/8, 2016 at 8:31 Comment(2)
I think this is slightly misleading. It suggests the only reason for needing lateinit is to give a hint to the compiler when it's not smart enough to figure out if a property is initialized. Another totally valid use case (which would be especially useful for primitives) is to ensure that a descriptive exception is eagerly thrown if client code tries to access a value before it has been initialized/calculated, rather than silently doing the wrong thing based on a default value of 0 or false.Referee
@MikeRippon, while that is a possible reason to use it, it is not the intended use by the designers of Kotlin. For that use, they provide Delegates.notNull(). They kept lateinit kind of low-level, high performance, and Java-reflection-friendly. Having these two different features is necessary so we can have working non-nullability with classes that are injected via Java libraries.Wigwam
C
-3

I think that in case of primitives it's less resources taking to simply initialise it to let me say 0 and hold the simple value in memory rather than store extra information about the object nullability which is used by lateinit mechanism.

Correct me if it's not the case.

Cyrie answered 6/5, 2019 at 11:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.