Dans Kotlin, si vous ne voulez pas initialiser une propriété de classe à l'intérieur du constructeur ou en haut du corps de classe, vous avez essentiellement ces deux options (à partir de la référence de langage):
lazy () est une fonction qui prend un lambda et retourne une instance de Lazy qui peut servir de délégué pour implémenter une propriété lazy: le premier appel à get () exécute le lambda passé à lazy () et se souvient du résultat, les appels suivants get () renvoie simplement le résultat mémorisé.
Exemple
public class Hello { val myLazyString: String by lazy { "Hello" } }
Ainsi, le premier appel et les appels secondaires, où qu'ils se trouvent, à myLazyString retourneront "Bonjour"
Normalement, les propriétés déclarées comme ayant un type non nul doivent être initialisées dans le constructeur. Cependant, cela n'est souvent pas pratique. Par exemple, les propriétés peuvent être initialisées via l'injection de dépendances ou dans la méthode de configuration d'un test unitaire. Dans ce cas, vous ne pouvez pas fournir un initialiseur non nul dans le constructeur, mais vous voulez toujours éviter les vérifications nulles lors du référencement de la propriété à l'intérieur du corps d'une classe.
Pour gérer ce cas, vous pouvez marquer la propriété avec le modificateur Lateinit:
public class MyTest { lateinit var subject: TestSubject @SetUp fun setup() { subject = TestSubject() } @Test fun test() { subject.method() } }
Le modificateur ne peut être utilisé que sur les propriétés var déclarées à l'intérieur du corps d'une classe (pas dans le constructeur principal), et uniquement lorsque la propriété n'a pas de getter ou de setter personnalisé. Le type de la propriété doit être non nul et ne doit pas être un type primitif.
Alors, comment choisir correctement entre ces deux options, car les deux peuvent résoudre le même problème?
la source
lateinit
expose son champ de support avec la visibilité du setter, de sorte que les façons d'accéder à la propriété depuis Kotlin et depuis Java sont différentes. Et à partir du code Java, cette propriété peut être définie mêmenull
sans aucun contrôle dans Kotlin. Par conséquent, celateinit
n'est pas pour l'initialisation paresseuse mais pour l'initialisation pas nécessairement à partir du code Kotlin.Lazy
+ explicitement stocké.isInitialized()
pour ce faire. Je suppose qu'il n'y a pas de moyen simple de vérifier une telle propriété ennull
raison de la garantie que vous ne pouvez pas en obtenirnull
. :) Voir cette démo .by lazy
pour ralentir le temps de construction ou d'exécution?lateinit
pour contourner l'utilisation denull
pour une valeur non initialisée. Autre que celanull
ne devrait jamais être utilisé, et avec deslateinit
valeurs nulles peut être éliminé. C'est comme ça que j'aime Kotlin :)En plus de
hotkey
la bonne réponse de, voici comment je choisis parmi les deux dans la pratique:lateinit
est pour l'initialisation externe: lorsque vous avez besoin de choses externes pour initialiser votre valeur en appelant une méthode.par exemple en appelant:
While
lazy
est lorsqu'il n'utilise que des dépendances internes à votre objet.la source
Réponse très courte et concise
Lateinit: il initialise récemment des propriétés non nulles
Contrairement à l'initialisation paresseuse, lateinit permet au compilateur de reconnaître que la valeur de la propriété non nulle n'est pas stockée dans l'étape constructeur pour compiler normalement.
Initialisation paresseuse
par lazy peut être très utile lors de l'implémentation de propriétés en lecture seule (val) qui effectuent l'initialisation paresseuse dans Kotlin.
by lazy {...} effectue son initialiseur là où la propriété définie est d'abord utilisée, pas sa déclaration.
la source
Lateinit vs paresseux
Lateinit
i) Utilisez-le avec la variable mutable [var]
ii) Autorisé avec uniquement des types de données non nullables
iii) C'est une promesse au compilateur que la valeur sera initialisée à l'avenir.
REMARQUE : si vous essayez d'accéder à la variable lateinit sans l'initialiser, elle lève UnInitializedPropertyAccessException.
paresseux
i) L'initialisation paresseuse a été conçue pour empêcher l'initialisation inutile des objets.
ii) Votre variable ne sera initialisée que si vous l'utilisez.
iii) Il n'est initialisé qu'une seule fois. La prochaine fois que vous l'utiliserez, vous obtiendrez la valeur de la mémoire cache.
iv) Il est sûr pour les threads (il est initialisé dans le thread où il est utilisé pour la première fois. D'autres threads utilisent la même valeur stockée dans le cache).
v) La variable ne peut être que val .
vi) La variable ne peut être que non nulle .
la source
En plus de toutes les bonnes réponses, il existe un concept appelé chargement paresseux:
En l'utilisant correctement, vous pouvez réduire le temps de chargement de votre application. Et l'implémentation de Kotlin consiste à
lazy()
charger la valeur nécessaire dans votre variable à tout moment.Mais lateinit est utilisé lorsque vous êtes sûr qu'une variable ne sera ni nulle ni vide et sera initialisée avant de l'utiliser -eg dans la
onResume()
méthode pour android- et donc vous ne voulez pas la déclarer comme un type nullable.la source
onCreateView
,onResume
et d'autres aveclateinit
, mais parfois des erreurs s'y sont produites (car certains événements ont commencé plus tôt). Alors peut-êtreby lazy
peut donner un résultat approprié. J'utiliselateinit
pour des variables non nulles qui peuvent changer au cours du cycle de vie.Tout est correct ci-dessus, mais l'un des faits explication simple LAZY ---- Il y a des cas où vous voulez retarder la création d'une instance de votre objet jusqu'à sa première utilisation. Cette technique est connue sous le nom d'initialisation paresseuse ou d'instanciation paresseuse. Le but principal de l'initialisation paresseuse est d'augmenter les performances et de réduire l'empreinte mémoire. Si l'instanciation d'une instance de votre type entraîne un coût de calcul important et que le programme risque de ne pas l'utiliser réellement, vous voudriez retarder ou même éviter de gaspiller les cycles CPU.
la source
Si vous utilisez un conteneur Spring et que vous souhaitez initialiser un champ de bean non nullable,
lateinit
est mieux adapté.la source
@Autowired lateinit var myBean: MyBean
Si vous utilisez une variable immuable, il est préférable d'initialiser avec
by lazy { ... }
ouval
. Dans ce cas, vous pouvez être sûr qu'il sera toujours initialisé en cas de besoin et au maximum 1 fois.Si vous voulez une variable non nulle, qui peut changer sa valeur, utilisez
lateinit var
. Dans le développement Android , vous pouvez ensuite l' initialiser dans ces événements commeonCreate
,onResume
. Sachez que si vous appelez une requête REST et accédez à cette variable, cela peut conduire à une exceptionUninitializedPropertyAccessException: lateinit property yourVariable has not been initialized
, car la requête peut s'exécuter plus rapidement que cette variable ne pourrait s'initialiser.la source