new lazy properties kotlin

properties - lazy - Kotlin-Inicialización de la propiedad usando "por perezoso" frente a "lateinit"



new in kotlin (8)

En Kotlin si no desea inicializar una propiedad de clase dentro del constructor o en la parte superior del cuerpo de la clase, tiene básicamente estas dos opciones (de la referencia del lenguaje):

  1. Inicialización perezosa

lazy () es una función que toma un lambda y devuelve una instancia de Lazy que puede servir como delegado para implementar una propiedad lazy: la primera llamada a get () ejecuta el lambda pasado a lazy () y recuerda el resultado, llamadas posteriores para obtener () simplemente devuelve el resultado recordado.

Ejemplo

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

Entonces, la primera llamada y las llamadas subsecuentes, donde sea que esté, a myLazyString devolverán "Hola"

  1. Inicialización tardía

Normalmente, las propiedades declaradas como de tipo no nulo deben inicializarse en el constructor. Sin embargo, con bastante frecuencia esto no es conveniente. Por ejemplo, las propiedades se pueden inicializar mediante la inyección de dependencia o en el método de configuración de una prueba unitaria. En este caso, no puede proporcionar un inicializador no nulo en el constructor, pero aún así desea evitar verificaciones nulas cuando haga referencia a la propiedad dentro del cuerpo de una clase.

Para manejar este caso, puede marcar la propiedad con el modificador lateinit:

public class MyTest { lateinit var subject: TestSubject @SetUp fun setup() { subject = TestSubject() } @Test fun test() { subject.method() } }

El modificador solo se puede usar en propiedades var declaradas dentro del cuerpo de una clase (no en el constructor primario), y solo cuando la propiedad no tiene un captador o definidor personalizado. El tipo de la propiedad debe ser no nulo y no debe ser un tipo primitivo.

Entonces, ¿cómo elegir correctamente entre estas dos opciones, ya que ambas pueden resolver el mismo problema?


Además de la buena respuesta de la hotkey de hotkey , así es como elijo entre los dos en la práctica:

lateinit es para inicialización externa: cuando necesita material externo para inicializar su valor llamando a un método.

por ejemplo llamando a:

private lateinit var value: MyClass fun init(externalProperties: Any) { value = somethingThatDependsOn(externalProperties) }

Mientras que lazy es cuando solo usa dependencias internas de su objeto.


Además de todas las excelentes respuestas, hay un concepto llamado carga diferida:

La carga diferida es un patrón de diseño utilizado comúnmente en la programación de computadoras para diferir la inicialización de un objeto hasta el punto en el que se necesita.

Utilizándolo correctamente, puede reducir el tiempo de carga de su aplicación. Y la forma de implementación de Kotlin es mediante lazy() que carga el valor necesario en su variable siempre que sea necesario.

Pero lateinit se usa cuando está seguro de que una variable no será nula o vacía y se inicializará antes de usarla onResume() en el método onResume() para Android- y, por lo tanto, no desea declararla como un tipo anulable.


Estas son las diferencias significativas entre lateinit var y by lazy { ... } propiedad delegada:

  • lazy { ... } delegado lazy { ... } solo se puede usar para propiedades val , mientras que lateinit solo se puede aplicar a var s, porque no se puede compilar en un campo final , por lo que no se puede garantizar la inmutabilidad;

  • lateinit var tiene un campo de respaldo que almacena el valor, y by lazy { ... } crea un objeto delegado en el que el valor se almacena una vez calculado, almacena la referencia a la instancia delegada en el objeto de clase y genera el captador de la propiedad eso funciona con la instancia de delegado. Entonces, si necesita el campo de respaldo presente en la clase, use lateinit ;

  • Además de val s, lateinit no se puede usar para propiedades anulables y tipos primitivos de Java (esto se debe a que null usa para valores no inicializados);

  • lateinit var se puede inicializar desde cualquier lugar desde el que se vea el objeto, por ejemplo, desde el interior de un código marco, y son posibles múltiples escenarios de inicialización para diferentes objetos de una sola clase. by lazy { ... } , a su vez, define el único inicializador de la propiedad, que solo puede modificarse anulando la propiedad en una subclase. Si desea que su propiedad se inicialice desde el exterior de una manera probablemente desconocida de antemano, use lateinit .

  • La inicialización by lazy { ... } es segura para subprocesos de forma predeterminada y garantiza que el inicializador se invoque como máximo una vez (pero esto puede alterarse utilizando otra sobrecarga lazy ). En el caso de lateinit var , depende del código del usuario inicializar la propiedad correctamente en entornos de subprocesos múltiples.

  • Una instancia de Lazy se puede guardar, transmitir e incluso usar para múltiples propiedades. Por el contrario, lateinit var s no almacena ningún estado de tiempo de ejecución adicional (solo null en el campo para el valor no inicializado).

  • Si tiene una referencia a una instancia de Lazy , isInitialized() permite verificar si ya se ha inicializado (y puede obtener dicha instancia con reflejo de una propiedad delegada). Para verificar si se ha inicializado una propiedad lateinit, puede usar property::isInitialized desde Kotlin 1.2 .

  • Una lambda transmitida by lazy { ... } puede capturar referencias del contexto donde se utiliza en su closure . Luego almacenará las referencias y las liberará solo una vez que la propiedad se haya inicializado. Esto puede conducir a que las jerarquías de objetos, como las actividades de Android, no se publiquen durante demasiado tiempo (o nunca, si la propiedad permanece accesible y nunca se accede a ella), por lo que debe tener cuidado con lo que usa dentro del lambda inicializador.

Además, hay otra forma no mencionada en la pregunta: Delegates.notNull() , que es adecuada para la inicialización diferida de propiedades no nulas, incluidas las de los tipos primitivos de Java.


Respuesta muy breve y concisa

lateinit: Inicializa propiedades no nulas últimamente

A diferencia de la inicialización diferida, lateinit permite al compilador reconocer que el valor de la propiedad no nula no se almacena en la etapa de construcción para compilar normalmente.

Inicialización perezosa

by lazy puede ser muy útil al implementar propiedades de solo lectura (val) que realizan la inicialización diferida en Kotlin.

by lazy {...} realiza su inicializador donde se usa por primera vez la propiedad definida, no su declaración.


Si está utilizando Spring container y desea inicializar el campo de frijoles no anulables, lateinit es más adecuado.

@Autowired lateinit var myBean: MyBean


Si usa una variable inalterable, entonces es mejor inicializar con by lazy { ... } o val . En este caso, puede estar seguro de que siempre se inicializará cuando sea necesario y, como máximo, 1 vez.

Si desea una variable no nula, que puede cambiar su valor, use lateinit var . En el desarrollo de Android, luego puede inicializarlo en eventos como onCreate , onResume . Tenga en cuenta que si llama a la solicitud REST y accede a esta variable, puede provocar una excepción UninitializedPropertyAccessException: lateinit property yourVariable has not been initialized , porque la solicitud puede ejecutarse más rápido de lo que esa variable podría inicializar.


El crédito va a @Amit Shekhar

lateinit

lateinit es una inicialización tardía.

Normalmente, las propiedades declaradas como de tipo no nulo deben inicializarse en el constructor. Sin embargo, con bastante frecuencia esto no es conveniente. Por ejemplo, las propiedades se pueden inicializar mediante la inyección de dependencia o en el método de configuración de una prueba unitaria. En este caso, no puede proporcionar un inicializador no nulo en el constructor, pero aún así desea evitar verificaciones nulas cuando haga referencia a la propiedad dentro del cuerpo de una clase.

Ejemplo:

public class Test { lateinit var mock: Mock @SetUp fun setup() { mock = Mock() } @Test fun test() { mock.do() } }

perezoso

perezoso es la inicialización perezosa.

lazy() es una función que toma un lambda y devuelve una instancia de lazy que puede servir como delegado para implementar una propiedad lazy: la primera llamada a get() ejecuta el lambda pasado a lazy() y recuerda el resultado, llamadas posteriores para get() simplemente devuelve el resultado recordado.

Ejemplo:

public class Example{ val name: String by lazy { “Amit Shekhar” } }


lateinit vs perezoso

  1. lateinit

    i) Úselo con la variable mutable [var]

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

    ii) Permitido solo con tipos de datos no anulables

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

    iii) Es una promesa compilar que el valor se inicializará en el futuro.

NOTA : Si intenta acceder a la variable lateinit sin inicializarla, arroja UnInitializedPropertyAccessException.

  1. perezoso

    i) La inicialización diferida se diseñó para evitar la inicialización innecesaria de objetos.

    ii) Su variable no se inicializará a menos que la use.

    iii) Se inicializa solo una vez. La próxima vez que lo use, obtendrá el valor de la memoria caché.

    iv) Es seguro para subprocesos (se inicializa en el subproceso donde se usa por primera vez. Otros subprocesos usan el mismo valor almacenado en la memoria caché).

    v) La variable puede ser var o val .

    vi) La variable puede ser anulable o no anulable .