クラスの定義
例: Player クラスを定義する
class Player {
var name = "jack"
}
[Show Kotlin Bytecode] (Shift キーを 2 回押します) を使用して、[Compile] をクリックします。
例 2: Player オブジェクトを作成し、name 属性に値を割り当てる
class Player {
var name : String?= "jack"
}
fun main() {
val p = Player()
p.name = "rose" // 这种赋值方式其实调用的是 setName() 方法
}
分野
定義した属性ごとに、Kotlin はフィールド、取得、およびセットを生成します。フィールドは属性データの保存に使用されます。フィールドを直接定義することはできません。Kotlin はフィールド内のデータを保護するためにフィールドをカプセル化し、gett と set にのみ公開します。属性の get メソッドにより、属性値の読み取り方法が決まります。各属性には get メソッドがあります。set メソッドはプロパティに値を割り当てる方法を決定するため、変更可能なプロパティのみが set メソッドを持ちます。Kotlin はデフォルトの get メソッドと set メソッドを自動的に提供しますが、プロパティ データの読み取りおよび書き込み方法を制御する必要がある場合は、それらをカスタマイズできます。
自動的に生成された get()/set() をオーバーライドするために独自の get()/set() メソッドを定義する必要がある場合は、フィールド属性を使用する必要があります。
計算されたプロパティ
計算されたプロパティは、オーバーライドする get または set 演算子によって定義されます。この場合、フィールドは必要ありません。
val rolledValue
get() = (1..6).shuffled.first()
ここではフィールドは使用しません。get() を呼び出すとき、(1..6).shuffled.first() の結果は、rolledValue には影響しません。
競合状態を防ぐ
クラス属性が nullable と mutable の両方である場合は、それを参照する前に null でないことを確認する必要があります。これを行う 1 つの方法は、これも標準関数を使用することです。
例:
var words : String? = "hello"
fun saySomething(){
words?.also {
println("Hello ${it.toUpperCase()}")
}
}
初期化
メインコンストラクター
Player クラスの定義ヘッダーでメイン コンストラクターを定義し、一時変数を使用して Player のさまざまなプロパティの初期値を提供します。Kotlin では、簡単に識別できるように、一時変数 (一度だけ参照されるパラメーターを含む) には通常、アンダースコアで始まる名前が付けられます。
Java では、コンストラクターの名前はクラス名と同じです。
メインコンストラクターでプロパティを定義する
Kotlin では、一時変数を使用する代わりに、パラメーターとクラス属性の両方を定義内で直接指定できます。通常、この方法ではコードの重複が減るため、クラス プロパティをより細かく定義できます。
二次コンストラクター
プライマリ コンストラクターがあるところには必ずセカンダリ コンストラクターがあります。プライマリ コンストラクターに対応するのはセカンダリ コンストラクターです。複数のセカンダリ コンストラクターを定義して、さまざまなパラメーターの組み合わせを構成できます。
セカンダリ コンストラクターを使用して初期化コード ロジックを定義します。
デフォルトパラメータ
コンストラクターを定義するときに、コンストラクターのパラメーターのデフォルト値を指定できます。呼び出し時にユーザーが値パラメーターを指定しない場合は、このデフォルト値が使用されます。
初期化ブロック
初期化ブロックは、変数または値を設定し、コンストラクターに渡された値が有効かどうかのチェックなどの妥当性チェックを実行できます。初期化ブロックのコードは、クラス インスタンス を構築するとき、つまりコンストラクター内で実行されます。
init {
require(age > 0){"age must be positive"}
require(name.isNotBlank()){"player must have a name."}
}
初期化シーケンス
1. メイン コンストラクターで宣言された属性、2. クラス レベルの属性の割り当て、3. init 初期化ブロックでの属性の割り当てと関数呼び出し、4. セカンダリ コンストラクターでの属性の割り当てと関数呼び出し。
拡張初期化
使用時のみ初期化されます。lateinitキーワードを使用することは、使用する前に初期化を行うという規約を作成することと同じです。
例: ツールを使用すると、ツールが初期化されます。つまり、main() メソッドで ready() が呼び出されたときに初期化されます。
class Student {
// lateinit 可以使变量延长初始化
lateinit var tool : String
fun ready(){
tool = "pen"
}
fun examination(){
println(tool)
}
}
fun main() {
val student = Student()
// 先 准备 笔
student.ready()
// 然后才能考试
student.examination()
}
ready() より前に検査() が呼び出された場合、例外がスローされます。拡張初期化変数が初期化されていないことを示します。
上記の例外に対して、lateinit 変数が初期化されているかどうかを確認できない限り、isInitializedチェックを実行できます。
遅延初期化
遅延初期化は初期化を延期する唯一の方法ではありません。初めて使用するまで変数を一時的に初期化しないこともできます。これは遅延初期化と呼ばれます。
初期化トラップ1
初期化ブロックを使用する場合、順序は非常に重要であり、ブロック内のすべてのプロパティが初期化されていることを確認する必要があります。
例:
上記の初期化シーケンスでは、クラスレベルの属性が初期化ブロック属性の前にあることがわかっているので、上記の書き方次第では大きな問題はありません。そのとき、逆コンパイル (kotlin Bytecoe を表示) したところ、思っていたものと異なることがわかりました。
konlin でのコンパイルは上から下に行われます。したがって、上記のコードでは、blood の宣言が init{} ブロックの後にあるため、コンパイル エラーが発生します (これは単なるコンパイルであり、属性の初期化ではないため、この 2 つを混同しないでください)。
正しい書き方は以下の通りです。
初期化トラップ2
次のコードは、コンパイラが init ブロックで name 属性が初期化されていることを認識するため、コンパイル時には問題ありませんが、コードが実行されるとすぐに、name 属性がまだ初期化されていないため、ヌル ポインタ例外がスローされます。値を割り当て、 firstLetter() 関数でそれを適用します。
上記の問題は、init{} ブロック内の 2 行のコードの順序を変更することで解決できます。
初期化トラップ3
コンパイラはすべてのプロパティが初期化されていることを認識するため、コードは正常にコンパイルされますが、実行結果は null になります。何が問題ですか? InitialPlayerName 関数を使用して playerName を初期化するとき、name 属性はまだ初期化されていません。