Property initialization using “by lazy” vs. “lateinit”

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

1. Lazy Initialization

lazy() is a function that takes a lambda and returns an case of Lazy which can serve as a delegate for enforcing a lazy property the first call to get() executes the lambda passed to lazy() and remembers the resultposterior calls to get() simply return the remembered result.

public class Hello { 
     val sampleLazyString: String by lazy { "Hello" } 

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

2. Late Initialization

Typicallyparcels declared as having anon-null type must be initialized in the constructor. stillfairly frequently this isn’t accessible. For illustrationparcels can be initialized through reliance injection, or in the setup system of a unit test. In this case, you can not supply anon-null initializer in the constructor, but you still want to avoid null checks when representing 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 parcels declared inside the body of a class( not in the primary constructor), and only when the property doesn’t have a custom getter or setter. The type of the property must benon-null, and it mustn’t be a primitive type.

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


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

  • lazy{.} delegate can only be used for val parcels, whereas lateinit can only be applied to vars, because it can not be collected to a final fieldtherefore no invariability 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 formerly calculatedstores the reference to the delegate case in the class object and generates the getter for the property that works with the delegate caseSo if you need the backing field present in the classuse lateinit;
    In addition to vals, lateinit can not be used for nullable parcels 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 frame law, and multiple initialization scripts 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 booting the property in asubclass.However, use lateinit, If you want your property to be initialized from outdoors in a way presumably unknown beforehand.
  • Initialization by lazy{.} is threadsafe by dereliction and guarantees that the initializer is invoked at most formerly( but this can be altered by using another lazy load). In the case of lateinit var, it’s over to the stoner‘s law to initialize the property rightly inmulti-threaded surroundings.
  • A Lazy case can be savedpassed around and indeed used for multiple parcels. On negative, lateinit vars don’t store any fresh runtime stateonly null in the field for uninitialized value).
    Still, isInitialized() allows you to check whether it has formerly been initialized( and you can gain similar case with reflection from a delegated property), If you hold a reference to an case of Lazy. To check whether a lateinit property has been initialized, you can use propertyisInitialized since Kotlin1.2.
  • A lambda passed to by lazy{.} may capture references from the environment where it’s used into its check. It’ll also store the references and release them only formerly the property has been initialized. This may lead to object scalessimilar as Android conditioning, not being released for too long( or ever, if the property remains accessible and is noway penetrated), so you should be careful about what you use inside the initializer lambda.

Also, there is another way not mentioned in the questionDelegates.notNull(), which is suitable for remitted initialization ofnon-null parcelsincluding those of Java primitive types.

Submit a Comment

Your email address will not be published. Required fields are marked *


Select Categories