web-dev-qa-db-ja.com

Kotlin:コンストラクターでクラス属性を初期化する

クラス属性を使用してKotlinクラスを作成します。これをコンストラクターで初期化します。

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)
    }
}

残念ながら、「?」で属性をNullableとして宣言する必要があります。ただし、属性はコンストラクターで初期化されます。この属性をNullable-attributeとして宣言すると、常に「!!」を使用してNonNull値を強制する必要があります。または、「?」でヌルチェックを提供します。

クラス属性がコンストラクタで初期化される場合、これを回避する方法はありますか?私はこのような解決策を高く評価したいと思います:

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)
    }
}
17
Christopher

コンストラクターで行うのが割り当てだけの場合は、プライベートプロパティを持つプライマリコンストラクターを使用できます。

例えば:

public class TestClass(private val context: Context) {

  public fun doSomeVoodoo() {
     val text = context.getString(R.string.abc_...)
  }
}
20
D3xter

D3xterで示されているように、コンストラクターで設定するオプションがあります。他のオプションもあります。ここにすべてが...

(@ D3xterに従って)コンストラクター内でプロパティを作成しますこれは、プライマリコンストラクターによって直接初期化された単純なプロパティの最も一般的なケースです

_class TestClass(private val context: Context) {
    fun doSomeVoodoo() {
        val text : String = context.getString()
    } 
}
_

valプロパティを宣言して初期化しないこともできます。可能なすべてのコンストラクタが実際に初期化すると仮定します(質問の2番目の例に従って)。 これは、値を別々に初期化できる複数のコンストラクタがある場合に正常です

_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()
    }
}
_

プロパティ宣言ではないコンストラクタパラメータを渡して、プロパティ初期化内で使用できます。 これは、より複雑な初期化がある場合、または委任されたプロパティを使用する必要がある場合に一般的です

_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()
    }
}
_

lateinit modifier を使用すると、構築中に値を初期化できませんが、最初の読み取りアクセスの前にそれが確実に実行されます。 これは、依存関係注入、IoCコンテナー、または何かがクラスの空のバージョンを作成し、すぐに初期化する場合に一般的です

_class TestClass() {
    private lateinit var context : Context // set by something else after construction

    fun doSomeVoodoo() {
        val text : String = context.getString()
    }
}
_

lateinitの場合、プロパティは現在varである必要があり、プリミティブ型では機能しません。

Delegates.notNull()などの目的で設計されたデリゲートを使用する場合は、varプロパティを宣言して初期化しないこともできます。 これはlateinitに似ており、初期状態がないvarが必要な場合に一般的ですが、後で不明な時点で設定されます

_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()
    }
}
_
19
Jayson Minard

構築後にオブジェクトを保持したくないという同様の問題がありました。 lazyまたはlateinitを使用すると、バイトコードが非効率になったため、いくつかの調査を行った後、このアプローチに落ち着き、それが役立つ場合に備えて回答を投稿しました。

解決

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
    }
}

または、上記をさらに簡略化して次のようにすることもできます。

class TestClass(context: Context) {
    private val homeDescription: String = context.getString(R.string.abc_action_bar_home_description)

    fun doSomeVoodoo() {
        val text : String = homeDescription
    }
}

逆コンパイルされたバイトコード

そして、逆コンパイルされたJavaこのバージョンは、他のアプローチよりも少し許容できると感じており、構築後にコンテキストへの参照は保持されません。

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;
    }
}
1
gMale