Kotlin lateinit var is initialized

Отложенная инициализация(by lazy, lateinit)

Ключевое слово by lazy служит для отложенной инициализации через механизм делегатов. Делегат lazy принимает лямбда-выражение с кодом, который вы бы хотели выполнить для инициализации свойства.

Иногда требуется объявить переменную, но отложить инициализацию. Причины задержки инициализации могут быть самыми разными. Например, для инициализации требуются предварительные вычисления, которые желательно не выполнять, если их результат никогда не будет использован. Обычно в таких случаях переменную объявляют через var, которая получает значение null, пока не будет инициализирована нужным значением, которое потом никогда не меняется.

 var catName: String? = null . catName = getName() // присваиваем значение 

Это неудобно, если вы знаете, что у переменной будет конкретное значение и вы хотите избегать значения null. В нашем примере переменная имеет тип String?, который поддерживает значение null, хотя могла бы иметь тип String. Как вариант, можно было использовать заданное значение, например:

 var catName: String = "NOT_INITIALIZED" // или пустая строка var catName: String = "" . catName = getValue() 

В любом случае приходится использовать var, даже зная, что после инициализации значение переменной никогда не изменится. Kotlin предлагает для подобных случаев использовать by lazy:

 val catName: String by lazy

В этом случае функция getName() будет вызвана только один раз, при первом обращении к catName. Вместо лямбда-выражения можно также использовать ссылку на функцию:

 val catName: String by lazy(::getName) 
 val infoTextView by lazy < view. findViewById(R.id.textView) > 

Модификатор lateinit

Иногда переменную нельзя сразу инициализировать, сделать это можно чуть позже. Для таких случаев придумали новый модификатор lateinit (отложенная инициализация). Это относится только к изменяемым переменным.

 private lateinit var button: Button 

Переменная обязательно должна быть изменяемой (var). Не должна относиться к примитивным типам (Int, Double, Float и т.д). Не должна иметь собственных геттеров/сеттеров.

Подобный подход удобен во многих случаях, избегая проверки на null. В противном случае пришлось бы постоянно использовать проверку или утверждение !!, что засоряет код.

Если вы обратитесь к переменной до её инициализации, то получите исключение «lateinit property . hos not been initialized» вместо NullPointerException.

В любое объявление var-свойства можно добавить ключевое слово lateinit. Тогда Kotlin позволит отложить инициализацию свойства до того момента, когда такая возможность появится. Это полезная возможность, но её следует применять осторожно. Если переменная с поздней инициализацией получит начальное значение до первого обращения к ней, проблем не будет. Но если сослаться на такое свойство до его инициализации, то получим исключение UninitializedPropertyAccessException. Как вариант, можно использовать тип с поддержкой null, но тогда придётся обрабатывать возможное значение null по всему коду. Получив начальное значение, переменные с поздней инициализацией будут работать так же, как другие переменные.

Читайте также:  Triangle area in java

Для проверки факта инициализации переменной вызывайте метод isInitialized(). Функцию следует использовать экономно — не следует добавлять эту проверку к каждой переменной с поздней инициализацией. Если вы используете isInitialized() слишком часто, то скорее всего вам лучше использовать тип с поддержкой null.

 lateinit var catName: String override fun onCreate(savedInstanceState: Bundle?) < super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) catName = "Barsik" if (::catName.isInitialized) < Log.d("Kot", "Hi, $catName") >> 

Источник

Properties

Properties in Kotlin classes can be declared either as mutable, using the var keyword, or as read-only, using the val keyword.

To use a property, simply refer to it by its name:

Getters and setters

The full syntax for declaring a property is as follows:

The initializer, getter, and setter are optional. The property type is optional if it can be inferred from the initializer or the getter’s return type, as shown below:

var initialized = 1 // has type Int, default getter and setter // var allByDefault // ERROR: explicit initializer required, default getter and setter implied

The full syntax of a read-only property declaration differs from a mutable one in two ways: it starts with val instead of var and does not allow a setter:

val simple: Int? // has type Int, default getter, must be initialized in constructor val inferredType = 1 // has type Int and a default getter

You can define custom accessors for a property. If you define a custom getter, it will be called every time you access the property (this way you can implement a computed property). Here’s an example of a custom getter:

You can omit the property type if it can be inferred from the getter:

If you define a custom setter, it will be called every time you assign a value to the property, except its initialization. A custom setter looks like this:

By convention, the name of the setter parameter is value , but you can choose a different name if you prefer.

If you need to annotate an accessor or change its visibility, but you don’t want to change the default implementation, you can define the accessor without defining its body:

var setterVisibility: String = «abc» private set // the setter is private and has the default implementation var setterWithAnnotation: Any? = null @Inject set // annotate the setter with Inject

Backing fields

In Kotlin, a field is only used as a part of a property to hold its value in memory. Fields cannot be declared directly. However, when a property needs a backing field, Kotlin provides it automatically. This backing field can be referenced in the accessors using the field identifier:

var counter = 0 // the initializer assigns the backing field directly set(value) < if (value >= 0) field = value // counter = value // ERROR StackOverflow: Using actual name ‘counter’ would make setter recursive >

Читайте также:  Regexp python только цифры

The field identifier can only be used in the accessors of the property.

A backing field will be generated for a property if it uses the default implementation of at least one of the accessors, or if a custom accessor references it through the field identifier.

For example, there would be no backing field in the following case:

Backing properties

If you want to do something that does not fit into this implicit backing field scheme, you can always fall back to having a backing property:

private var _table: Map? = null public val table: Map get() < if (_table == null) < _table = HashMap() // Type parameters are inferred >return _table ?: throw AssertionError(«Set to null by another thread») >

On the JVM: Access to private properties with default getters and setters is optimized to avoid function call overhead.

Compile-time constants

If the value of a read-only property is known at compile time, mark it as a compile time constant using the const modifier. Such a property needs to fulfil the following requirements:

  • It must be a top-level property, or a member of an object declaration or a companion object.
  • It must be initialized with a value of type String or a primitive type
  • It cannot be a custom getter

The compiler will inline usages of the constant, replacing the reference to the constant with its actual value. However, the field will not be removed and therefore can be interacted with using reflection.

Such properties can also be used in annotations:

const val SUBSYSTEM_DEPRECATED: String = «This subsystem is deprecated» @Deprecated(SUBSYSTEM_DEPRECATED) fun foo() < . >

Late-initialized properties and variables

Normally, properties declared as having a non-null type must be initialized in the constructor. However, it is often the case that doing so is not convenient. For example, properties can be initialized through dependency injection, or in the setup method of a unit test. In these cases, 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 such cases, you can mark the property with the lateinit modifier:

This modifier can 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), as well as for top-level properties and local variables. The type of the property or variable must be non-null, and it must not be a primitive type.

Accessing a lateinit property before it has been initialized throws a special exception that clearly identifies the property being accessed and the fact that it hasn’t been initialized.

Checking whether a lateinit var is initialized

To check whether a lateinit var has already been initialized, use .isInitialized on the reference to that property:

Читайте также:  Flash width height html

This check is only available for properties that are lexically accessible when declared in the same type, in one of the outer types, or at top level in the same file.

Overriding properties

Delegated properties

The most common kind of property simply reads from (and maybe writes to) a backing field, but custom getters and setters allow you to use properties so one can implement any sort of behavior of a property. Somewhere in between the simplicity of the first kind and variety of the second, there are common patterns for what properties can do. A few examples: lazy values, reading from a map by a given key, accessing a database, notifying a listener on access.

Such common behaviors can be implemented as libraries using delegated properties.

Источник

How to check if a lateinit variable is initialized in Kotlin?

In this short article, we are going to present how to find out that lateinit variable in Kotlin has been initialized.

2. Late-initialized variable

The non-null variables usually must be initialized in the constructor and this is the normal behavior in many programming languages, Kotlin is not an exception in this case. However, that approach is not so obvious in cases when variables are initialized through the dependency injection or other configuration methods. In such situations, we cannot add variable initializer in the constructor but still want to avoid constantly checking if variables are null in the class body.

To handle such cases Kotlin introduced the lateinit modifier:

In this example, the lateinitTest is marked with lateinit modifier. This adds some extra features to this variable.

3. Checking if the lateinit variable is initialized

We can check whether a lateinit variable has been initialized using the .isInitialized property:

class Lateinit < lateinit var lateinitTest: LateinitTest fun setup() < println(this::lateinitTest.isInitialized); lateinitTest = LateinitTest() >fun test() < println(this::lateinitTest.isInitialized); lateinitTest.method() >> fun main() < val lt = Lateinit(); lt.setup(); // false lt.test(); // true >

In this example before initializing the variable: lateinitTest = LateinitTest() the println(this::lateinitTest.isInitialized) command will display false . After lateinitTest variable has been initialized .isInitialized property will be presenting true . Note that using lateinit variable before initialization will results in UninitializedPropertyAccessException.

Note that lateinit modifier could be used only on non-primitive types and on property that does not have a custom getter or setter.

When we try to access a lateinit property before it has been initialized the special exception will be thrown with all information about what went wrong.

This will produce the following exception:

Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property lateinitTest has not been initialized at Lateinit.test(Lateinit.kt:12) at LateinitKt.main(Lateinit.kt:19) at LateinitKt.main(Lateinit.kt) 

4. Conclusion

In this short tutorial, we present a way to check if the lateinit variable has been initialized. Kotlin luckily provides a dedicated property that does this job well.

Источник

Оцените статью