Je crée une classe Kotlin avec un attribut de classe que je souhaite initialiser dans le constructeur:
public class TestClass {
private var context : Context? = null // Nullable attribute
public constructor(context : Context) {
this.context = context
}
public fun doSomeVoodoo() {
val text : String = context!!.getString(R.string.abc_action_bar_home_description)
}
}
Malheureusement, je dois déclarer l'attribut comme Nullable avec le "?" signe, bien que l'attribut sera initialisé dans le constructeur. Déclarer cet attribut en tant qu'attribut Nullable nécessite toujours de forcer une valeur NonNull avec "!!" ou pour fournir un null-check avec "?".
Existe-t-il un moyen d'éviter cela si l'attribut de classe est initialisé dans le constructeur? Je voudrais apprécier une solution comme celle-ci:
public class TestClass {
private var context : Context // Non-Nullable attribute
public constructor(context : Context) {
this.context = context
}
public fun doSomeVoodoo() {
val text : String = context.getString(R.string.abc_action_bar_home_description)
}
}
Si la seule chose que vous faites dans le constructeur est une affectation, vous pouvez utiliser le constructeur principal avec une propriété privée.
par exemple:
public class TestClass(private val context: Context) {
public fun doSomeVoodoo() {
val text = context.getString(R.string.abc_...)
}
}
Comme indiqué par D3xter, vous avez la possibilité de le définir dans le constructeur. Vous avez également d'autres options. Ici, ils sont tous ...
Créez la propriété dans le constructeur (selon @ D3xter), il s'agit du cas le plus courant de propriétés simples initialisées directement par le constructeur principal :
class TestClass(private val context: Context) {
fun doSomeVoodoo() {
val text : String = context.getString()
}
}
Vous pouvez déclarer la propriété val
et ne pas l'initialiser, en supposant que tous les constructeurs possibles l'initialisent réellement (conformément à votre deuxième exemple dans la question posée). Ceci est normal lorsque vous avez plus d'un constructeur qui pourrait initialiser une valeur différemment :
public class TestClass {
private val context: Context
public constructor(context : Context) {
this.context = context
}
// alternative constructor
public constructor(pre: PreContext) {
this.context = pre.readContext()
}
public fun doSomeVoodoo() {
val text : String = context.getString()
}
}
Vous pouvez transmettre des paramètres de constructeur qui ne sont pas des déclarations de propriété, puis les utiliser dans les initialisations de propriété. Ceci est courant lorsque vous avez des initialisations plus complexes ou devez utiliser des propriétés déléguées :
class TestClass(context: PreContext) {
private val context : Context by lazy { context.readContext() }
private val other: List<Items> = run {
context.items.map { it.tag }.filterNotNull()
}
private val simpleThing = context.getSimple()
fun doSomeVoodoo() {
val text : String = context.getString()
}
}
Utilisation de lateinit
modificateur lorsque vous ne pouvez pas initialiser la valeur pendant la construction, mais que vous êtes sûr que cela sera fait avant votre premier accès en lecture. Ceci est courant lorsqu'une injection de dépendance, un conteneur IoC ou quelque chose qui crée une version vide de votre classe puis l'initialise immédiatement :
class TestClass() {
private lateinit var context : Context // set by something else after construction
fun doSomeVoodoo() {
val text : String = context.getString()
}
}
Pour lateinit
, la propriété doit être actuellement une var
et ne fonctionne pas avec les types primitifs.
Vous pouvez également déclarer une propriété var
et ne pas l'initialiser si vous utilisez un délégué conçu à cet effet, tel que Delegates.notNull()
. Ceci est similaire à lateinit
et commun lorsque vous voulez une var
qui n'a pas d'état initial, mais est définie ultérieurement après la construction à un moment inconnu :
public class TestClass() {
private var context: Context by Delegates.notNull()
public fun doSomeVoodoo() {
// if context is not set before this is called, an exception is thrown
val text : String = context.getString()
}
}
J'ai eu un problème similaire où je ne voulais pas garder l'objet après la construction. L'utilisation de lazy
ou lateinit
a abouti à un bytecode inefficace. Après quelques recherches, j'ai choisi cette approche et suis revenue pour poster la réponse si cela peut aider:
class TestClass(context: Context) {
private val homeDescription: String
init {
homeDescription = context.getString(R.string.abc_action_bar_home_description)
}
fun doSomeVoodoo() {
val text : String = homeDescription
}
}
alternativement, ce qui précède peut être encore simplifié en:
class TestClass(context: Context) {
private val homeDescription: String = context.getString(R.string.abc_action_bar_home_description)
fun doSomeVoodoo() {
val text : String = homeDescription
}
}
Et la version Java décompilée de cette version semble un peu plus acceptable que les autres approches et aucune référence au contexte n’est retenue après la construction:
public final class TestClass {
private final String homeDescription;
public final void doSomeVoodoo() {
String text = this.homeDescription;
}
public TestClass(@NotNull Context context) {
Intrinsics.checkParameterIsNotNull(context, "context");
super();
String var10001 = context.getString(2131296256);
Intrinsics.checkExpressionValueIsNotNull(var10001, "context.getString(R.stri…ion_bar_home_description)");
this.homeDescription = var10001;
}
}