354

In Kotlin if you don't want to initialize a class property inside the constructor or in the top of the class body, you have basically these two options (from the language reference):

  1. Lazy Initialization

lazy() is a function that takes a lambda and returns an instance of Lazy<T> which can serve as a delegate for implementing a lazy property: the first call to get() executes the lambda passed to lazy() and remembers the result, subsequent calls to get() simply return the remembered result.

Example

public class Hello {

   val myLazyString: String by lazy { "Hello" }

}

So the first call and the subsequential calls, wherever it is, to myLazyString will return Hello

  1. Late Initialization

Normally, properties declared as having a non-null type must be initialized in the constructor. However, fairly often this is not convenient. For example, properties can be initialized through dependency injection, or in the setup method of a unit test. In this case, you cannot supply a non-null initializer in the constructor, but you still want to avoid null checks when referencing the property inside the body of a class.

To handle this case, you can mark the property with the lateinit modifier:

public class MyTest {
   
   lateinit var subject: TestSubject

   @SetUp fun setup() { subject = TestSubject() }

   @Test fun test() { subject.method() }
}

The modifier can only be used on var properties declared inside the body of a class (not in the primary constructor), and only when the property does not have a custom getter or setter. The type of the property must be non-null, and it must not be a primitive type.

So, how to choose correctly between these two options, since both of them can solve the same problem?

iknow
  • 4,796
  • 9
  • 19
  • 37
regmoraes
  • 4,027
  • 2
  • 19
  • 31

9 Answers9

427

Here are the significant differences between lateinit var and by lazy { ... } delegated property:

  • lazy { ... } delegate can only be used for val properties, whereas lateinit can only be applied to vars, because it can't be compiled to a final field, thus no immutability can be guaranteed;

  • lateinit var has a backing field which stores the value, and by lazy { ... } creates a delegate object in which the value is stored once calculated, stores the reference to the delegate instance in the class object and generates the getter for the property that works with the delegate instance. So if you need the backing field present in the class, use lateinit;

  • In addition to vals, lateinit cannot be used for nullable properties or Java primitive types (this is because of null used for uninitialized value);

  • lateinit var can be initialized from anywhere the object is seen from, e.g. from inside a framework code, and multiple initialization scenarios are possible for different objects of a single class. by lazy { ... }, in turn, defines the only initializer for the property, which can be altered only by overriding the property in a subclass. If you want your property to be initialized from outside in a way probably unknown beforehand, use lateinit.

  • Initialization by lazy { ... } is thread-safe by default and guarantees that the initializer is invoked at most once (but this can be altered by using another lazy overload). In the case of lateinit var, it's up to the user's code to initialize the property correctly in multi-threaded environments.

  • A Lazy instance can be saved, passed around and even used for multiple properties. On contrary, lateinit vars do not store any additional runtime state (only null in the field for uninitialized value).

  • If you hold a reference to an instance of Lazy, isInitialized() allows you to check whether it has already been initialized (and you can obtain such instance with reflection from a delegated property). To check whether a lateinit property has been initialized, you can use property::isInitialized since Kotlin 1.2.

  • A lambda passed to by lazy { ... } may capture references from the context where it is used into its closure.. It will then store the references and release them only once the property has been initialized. This may lead to object hierarchies, such as Android activities, not being released for too long (or ever, if the property remains accessible and is never accessed), so you should be careful about what you use inside the initializer lambda.

Also, there's another way not mentioned in the question: Delegates.notNull(), which is suitable for deferred initialization of non-null properties, including those of Java primitive types.

chornge
  • 1,615
  • 3
  • 22
  • 30
hotkey
  • 111,884
  • 27
  • 298
  • 285
  • 12
    Great answer! I would add that `lateinit` exposes its backing field with visibility of the setter so the ways the property is accessed from Kotlin and from Java are different. And from Java code this property can be set even to `null` without any checks in Kotlin. Therefore `lateinit` is not for the lazy initialization but for initialization not necessarily from Kotlin code. – Michael Apr 14 '16 at 13:04
  • Is there anything equivalent to Swift's "!" ?? In other words it's something that is late-initialized but CAN be checked for null without it failing. Kotlin's 'lateinit' fails with "lateinit property currentUser has not been initialized" if you check 'theObject == null'. This is super useful when you have an object that is not null in its core usage scenario (and thus want to code against an abstraction where it is non-null), but is null in exceptional/limited scenarios (ie: accessing the currently logged in user, which is never null except upon initial login / on login screen) – Marchy Feb 13 '17 at 15:42
  • @Marchy, you can use explicitly stored `Lazy` + `.isInitialized()` to do that. I guess there's no straightforward way to check such a property for `null` because of the guarantee that you cannot get `null` from it. :) See [this demo](http://try.kotlinlang.org/#/UserProjects/7qfc6epofflqpt3k8ts3mrsrqf/2ot3rul2qrvdqg3qd6uaomp6nl). – hotkey Feb 13 '17 at 15:54
  • 1
    I liked the idea of using `lateinit` to circumvent the use of `null` for uninitialized value. Other than that `null` should never be used, and with `lateinit` nulls can be eliminated away. That's how I love Kotlin :) – KenIchi Jan 08 '20 at 04:54
  • 1
    not `property::isInitialized` but `::property.isInitialized` – Konstantin Zyubin Dec 10 '20 at 07:29
55

lateinit vs lazy

  1. lateinit

    i) Use it with mutable variable[var]

     lateinit var name: String       //Allowed
     lateinit val name: String       //Not Allowed
    

ii) Allowed with only non-nullable data types

    lateinit var name: String       //Allowed
    lateinit var name: String?      //Not Allowed

iii) It is a promise to compiler that the value will be initialized in future.

NOTE: If you try to access lateinit variable without initializing it then it throws UnInitializedPropertyAccessException.

  1. lazy

    i) Lazy initialization was designed to prevent unnecessary initialization of objects.

ii) Your variable will not be initialized unless you use it.

iii) It is initialized only once. Next time when you use it, you get the value from cache memory.

iv) It is thread safe(It is initialized in the thread where it is used for the first time. Other threads use the same value stored in the cache).

v) The variable can only be val.

vi) The variable can only be non-nullable.

Yogesh Umesh Vaity
  • 16,749
  • 10
  • 82
  • 69
Geeta Gupta
  • 1,222
  • 9
  • 16
38

Additionnally to hotkey's good answer, here is how I choose among the two in practice:

lateinit is for external initialisation: when you need external stuff to initialise your value by calling a method.

e.g. by calling:

private lateinit var value: MyClass

fun init(externalProperties: Any) {
   value = somethingThatDependsOn(externalProperties)
}

While lazy is when it only uses dependencies internal to your object.

Guillaume
  • 21,938
  • 14
  • 54
  • 69
  • 1
    I think we could still lazy initialize even if it depends on an external object. Just need to pass the value to an internal variable. And use the internal variable during lazy initialization. But it's as natural as Lateinit though. – Elye May 18 '16 at 13:50
  • This approach throws UninitializedPropertyAccessException, I double checked that I am calling a setter function before using the value. Is there specific rule I am missing with lateinit? In your answer replace MyClass and Any with android Context, thats my case. – Talha Mar 12 '18 at 07:06
34

Very Short and concise Answer

lateinit: It initialize non-null properties lately

Unlike lazy initialization, lateinit allows the compiler to recognize that the value of the non-null property is not stored in the constructor stage to compile normally.

lazy Initialization

by lazy may be very useful when implementing read-only(val) properties that perform lazy-initialization in Kotlin.

by lazy { ... } performs its initializer where the defined property is first used, not its declaration.

Yogesh kataria
  • 1,193
  • 11
  • 17
  • great answer, especially the "performs its initializer where the defined property is first used, not its declaration" – user1489829 Mar 27 '20 at 18:55
9

In addition to all of the great answers, there is a concept called lazy loading:

Lazy loading is a design pattern commonly used in computer programming to defer initialization of an object until the point at which it is needed.

Using it properly, you can reduce the loading time of your application. And Kotlin way of it's implementation is by lazy() which loads the needed value to your variable whenever it's needed.

But lateinit is used when you are sure a variable won't be null or empty and will be initialized before you use it -e.g. in onResume() method for android- and so you don't want to declare it as a nullable type.

M Reza
  • 13,860
  • 13
  • 53
  • 64
  • 1
    Yes, I also initialized in `onCreateView`, `onResume` and other with `lateinit`, but sometimes errors occured there (because some events started earlier). So maybe `by lazy` can give an appropriate result. I use `lateinit` for non-null variables that can change during lifecycle. – CoolMind Jul 11 '19 at 08:44
4

Everything is correct above, but one of facts simple explanation LAZY----There are cases when you want to delay the creation of an instance of your object until its first usage. This technique is known as lazy initialization or lazy instantiation. The main purpose of lazy initialization is to boost performance and reduce your memory footprint. If instantiating an instance of your type carries a large computational cost and the program might end up not actually using it, you would want to delay or even avoid wasting CPU cycles.

user9830926
  • 245
  • 2
  • 3
0

If you are using Spring container and you want to initialize non-nullable bean field, lateinit is better suited.

    @Autowired
    lateinit var myBean: MyBean
Ninetou
  • 624
  • 1
  • 7
  • 15
mpprdev
  • 1,094
  • 1
  • 12
  • 25
0

If you use an unchangable variable, then it is better to initialize with by lazy { ... } or val. In this case you can be sure that it will always be initialized when needed and at most 1 time.

If you want a non-null variable, that can change it's value, use lateinit var. In Android development you can later initialize it in such events like onCreate, onResume. Be aware, that if you call REST request and access this variable, it may lead to an exception UninitializedPropertyAccessException: lateinit property yourVariable has not been initialized, because the request can execute faster than that variable could initialize.

CoolMind
  • 16,738
  • 10
  • 131
  • 165
0

Diff btw lateinit and lazy

lateinit

  1. Use only with mutable variable i.e. var and non-nullable data types

lateinit var name: String //Allowed with non-nullable

  1. You are telling the compiler that the value will be initialized in future.

NOTE: If you try to access lateinit variable without initializing it then it throws UnInitializedPropertyAccessException.

lazy

  1. Lazy initialization was designed to prevent unnecessary initialization of objects.

  2. Your variable will not be initialized unless you use it.

  3. It is initialized only once. Next time when you use it, you get the value from cache memory.

  4. It is thread safe.

  5. The variable can only be val and non-nullable.

Cheers :)