Java開発者として、バッキングフィールドの概念は私には少し異質です。
class Sample {
var counter = 0 // the initializer value is written directly to the backing field
set(value) {
if (value >= 0) field = value
}
}
このバッキングフィールドは何に適していますか? Kotlin docs 前述:Kotlinのクラスにフィールドを含めることはできません。ただし、カスタムアクセサを使用する場合は、バッキングフィールドが必要になる場合があります。どうして?セッター内でプロパティ名自体を使用することとの違いは何ですか?.
class Sample {
var counter = 0
set(value) {
if (value >= 0) this.counter = value // or just counter = value?
}
}
なぜなら、field
キーワードがない場合、get()
またはset(value)
の値を実際に設定/取得することはできないからです。これにより、カスタムアクセサーのバッキングフィールドにアクセスできます。
これは、同等のJavaサンプルのコード:
class Sample {
private int counter = 0;
public void setCounter(int value) {
if (value >= 0) setCounter(value);
}
public int getCounter() {
return counter;
}
}
セッターはそれ自体への無限の再帰であり、何も変更しないため、これは良くないようです。 foo.bar = value
PUTFIELD
の代わりにセッター呼び出しに変換されます。
編集:Javaにはフィールドがありますが、Kotlinにはプロパティがあり、これはフィールドよりもかなり高いレベルの概念です。
プロパティには2つのタイプがあります。1つはバッキングフィールドがあり、もう1つはありません。
バッキングフィールドを持つプロパティは、値をフィールドの形式で格納します。このフィールドにより、メモリに値を保存できます。そのようなプロパティの例は、first
のsecond
およびPair
プロパティです。そのプロパティは、Pair
のメモリ内表現を変更します。
バッキングフィールドのないプロパティは、メモリに直接保存する以外の方法で値を保存する必要があります。他のプロパティ、またはオブジェクト自体から計算する必要があります。このようなプロパティの例としては、indices
のList
拡張プロパティがあります。これは、フィールドではなく、size
プロパティに基づく計算結果です。したがって、List
のメモリ内表現は変更されません(Javaが静的に入力されるため、まったく実行できません)。
最初は、私もこの概念を理解するのに苦労しました。例を使用して説明します。
このKotlinクラスを検討してください
class DummyClass {
var size = 0;
var isEmpty
get() = size == 0
set(value) {
size = size * 2
}
}
コードを見ると、2つのプロパティ、つまりsize
(デフォルトアクセサー付き)とisEmpty
(カスタムアクセサー付き)があることがわかります。ただし、フィールドは1つだけです(つまり、size
)。フィールドが1つしかないことを理解するために、このクラスに相当するJavaを見てみましょう。
[ツール]-> [Kotlin]-> [Kotlin ByteCode in AndroidStudio。Decompileをクリックします。
public final class DummyClass {
private int size;
public final int getSize() {
return this.size;
}
public final void setSize(int var1) {
this.size = var1;
}
public final boolean isEmpty() {
return this.size == 0;
}
public final void setEmpty(boolean value) {
this.size *= 2;
}
}
明らかに、JavaクラスにはisEmpty
のゲッター関数とセッター関数のみがあり、そのために宣言されたフィールドはありません。同様に、Kotlinには、バッキングフィールドはありませんプロパティはそのフィールドにまったく依存しないため、プロパティisEmpty
です。
isEmpty
プロパティのカスタムゲッターとセッターを削除しましょう。
class DummyClass {
var size = 0;
var isEmpty = false
}
そして、Java上記のクラスに相当するものは
public final class DummyClass {
private int size;
private boolean isEmpty;
public final int getSize() {
return this.size;
}
public final void setSize(int var1) {
this.size = var1;
}
public final boolean isEmpty() {
return this.isEmpty;
}
public final void setEmpty(boolean var1) {
this.isEmpty = var1;
}
}
ここには、フィールドsize
とisEmpty
の両方があります。 isEmpty
プロパティのゲッターとセッターはそれに依存するため、isEmpty
はバッキングフィールドです。
バッキングフィールドは、検証を実行したり、状態が変化したときにイベントをトリガーしたりするのに適しています。コードをJava setter/getterに追加したときを考えてください。バッキングフィールドは同様のシナリオで役立ちます。セッター/ゲッター。
フィールドにフィールド名自体を割り当てる場合、実際にはセッター(つまりset(value)
)を呼び出しています。あなたが持っている例では、this.counter = value
は、スタックがオーバーフローするまでset(value)に再帰します。 field
を使用すると、セッター(またはゲッター)コードがバイパスされます。
私の理解は、プロパティ値を変更または使用するときにgetまたはsetのプロパティ値への参照としてfield identifierを使用することですgetまたはset。
例えば:
class A{
var a:Int=1
get(){return field * 2} // Similiar to Java: public int geta(){return this.a * 2}
set(value) {field = value + 1}
}
次に:
var t = A()
println(t.a) // OUTPUT: 2, equal to Java code: println(t.a * 2)
t.a = 2 // The real action is similar to Java code: t.a = t.a +1
println(t.a) // OUTPUT: 6, equal to Java code: println(t.a * 2)