Josh Skeen, David Greenhalgh著『Kotlinプログラミング』には章末問題がある。残念なことに解答がないので、第10章に引き続き本を読み進めながら答案を記述する。
もし回答に間違いや意見があれば、ぜひご指摘いただきたい。
第13章 章末問題 回答案
13.7 チャレンジ! エクスカリバーの謎
本書 リスト13-16,17の動作には少し悩んだので、復習を兼ねて次のコードを実行して初期化について挙動を確認した。(以下に示すD5クラス、D6クラスの動作が理解できていなかった。)
ポイントはfiledキーワードとプロパティー、初期化ブロック(init{...})に与えられた値の関係、挙動を理解することだと思います。コード横にコメントを記入しましたが、うまく説明ができませんでしたので、以下のコードをコンパイルして動作を確認してください。
C1クラス
C1.kt
fun main() {
// クラスC1のプロパティを呼び出す
println()
println("C1をインスタンス化")
val c1 = C1()
println("クラスC1インスタンス直後のpropty: ${c1.propty}") // abc
}
// ゲッター・セッターなし(ゲッター機能あり、セッター機能なし)
class C1 {
val propty = "abc"
}
C2クラス
C2.kt
fun main() {
// クラスC2のプロパティに書き込み、それを呼び出す
println()
println("C2をインスタンス化")
var c2 = C2() // 書き換えをするため変数定義のキーワードをvalからvarに変更
println("クラスC2インスタンス直後のpropty: ${c2.propty}") // abc
c2.propty = "def"
println("C2のpropty: ${c2.propty}") // def
}
// クラスプロパティにゲッターセッターを追加する
class C2 {
var propty = "abc" // 書き換えをするため変数定義のキーワードをvalからvarに変更
get() = field // fieldは、この場合、proptyのことである
set(value) { // valueは、この場合、セッターによって受け取る値である
field = value
}
}
C3クラス
C3.kt
fun main() {
// クラスC3のコンストラクタを用いて初期値を与える
println()
println("コンストラクタを用いてプロパティに初期値ghiを与える")
var c3 = C3("ghi")
println("クラスC3のpropty: ${c3.propty}") // ghi
println("クラスC3のproptyをセッターを用いてdefに書き換える")
c3.propty = "def"
println("クラスC3のpropty: ${c3.propty}") // def
println()
}
// クラスコンストラクタを追加する
class C3(_propty: String){ // _proptyはこの波括弧{...}内をスコープとする使い捨ての変数
var propty = _propty // コンストラクタを使ってコンストラクタで与えられた値_proptyを使って、proptyを初期化
get() = field // c3.proptyを要求された時に、filedを通してproptyの値が出力される
set(value) { // c3.propty="xxx"で値が与えられたときに、valueがxxxという値になり、fieldに値が渡される。このときfieldはproptyのことである。
field = value
}
}
D1クラス
D1.kt
fun main() {
println()
println("D1をインスタンス化")
val d1 = D1()
println("クラスD1インスタンス化直後に、ゲッターを通して得たpropty: ${d1.propty}") // Abc
}
// abcという文字列の先頭文字を大文字に変えて、proptyに与える。
class D1 {
val propty = "abc".capitalize()
}
D2クラス
D2.kt
fun main() {
//
println()
println("D2をインスタンス化")
var d2 = D2() // 書き換えをするため変数定義のキーワードをvalからvarに変更
println("クラスD2インスタンス化直後に、ゲッターを通して得たpropty: ${d2.propty}") // Abc
d2.propty = "mno"
println("D2のセッターにmnoを与えた直後に、ゲッターを通して得たpropty: ${d2.propty}") // Mnodef
}
// クラスプロパティにゲッターセッターを追加する
class D2 {
var propty = "abc"
get() = field.capitalize() // fieldはproptyを示し、この値を加工し、get()で出力する
set(value) {
field = value + "def" // セッターで受け取った値をvalueを通して加工して(この場合、valueにdefを加えて)、field(この場合propty)に渡す。
}
}
D3クラス
D3.kt
fun main() {
//
println()
println("D3のプライマリコンストラクタにjklを与えインスタンス化")
var d3 = D3("jkl")
println("クラスD3インスタンス化直後に、ゲッターを通して得たpropty: ${d3.propty}") // Jkl
d3.propty = "mno"
println("D3のセッターにmnoを与えた直後に、ゲッターを通して得たpropty: ${d3.propty}") // Mnodef
}
// クラスに引数ありのプライマリコンストラクタを追加する
class D3(_propty: String) {
var propty = _propty // コンストラクタを通して得た値_proptyをproptyに与える
get() = field.capitalize()
set(value) {
field = value + "def"
}
}
D4クラス
D4.kt
fun main() {
//
println()
println("D4コンストラクタにjklを与えインスタンス化")
var d4 = D4("jkl")
println("クラスD4インスタンス化直後に、ゲッターを通して得たpropty: ${d4.propty}") // Abc
d4.propty = "mno"
println("D4のセッターにmnoを与えた直後に、ゲッターを通して得たpropty: ${d4.propty}") // Mnodef
}
// 引数ありコンストラクタではあるが、受け取った_proptyはproptyに与えることなく、proptyにはabcを与える
class D4(_propty: String) {
var propty = "abc" // proptyにabcを与える
get() = field.capitalize()
set(value) {
field = value + "def"
}
}
D5クラス
D5.kt
fun main() {
//
println()
println("D5のプライマリコンストラクタにjklを与えインスタンス化")
var d5 = D5("jkl")
println("クラスD5インスタンス化直後に、ゲッターを通して得たpropty: ${d5.propty}") // Jkldef
d5.propty = "mno"
println("D5のセッターにmnoを与えた直後に、ゲッターを通して得たpropty: ${d5.propty}") // Mnodef
//
println()
println("D5 引数なしセカンドコンストラクタをインスタンス化")
var d51 = D5()
println("クラスD4(引数なしコンストラクタ)インスタンス化直後のプロパティ: ${d51.propty}") // Ghidef
d51.propty = "mno"
println("D5のセッターにmnoを与えた直後のプロパティー: ${d51.propty}") // Mnodef
}
// 初期化ブロックinit{...}を追加
class D5(_propty: String) {
var propty = "abc" // init初期化ブロックがあると、abcで初期化することはなく、
// init{...}ブロックの中の式に従って動作する。
get() = field.capitalize()
set(value) {
field = value + "def"
}
init{
propty = _propty // コンストラクタで与えられた値_proptyは、先ず直接proptyに代入されるのではなく、
// proptyのセッターに与えられる。すなわちvalueが_proptyになり、
// filedが_propty+"def"になり、最後にpropty(=filed)=_propty+"def"になる
// この動きがinit{...}の挙動を理解する上での難点であった
}
constructor() : this("ghi")
}
D6クラス
D6.kt
fun main() {
//
println()
println("D6のプライマリコンストラクタにjklを与えインスタンス化")
var d6 = D6("jkl")
println("クラスD6インスタンス化直後に、ゲッターを通して得たpropty: ${d6.propty}") // Jkldef
d6.propty = "mno"
println("D5のセッターにmnoを与えた直後に、ゲッターを通して得たpropty: ${d6.propty}") // Mnodef
}
// 初期化ブロックinit{...}を追加(D5クラスの"var propty ="で与える値が"abc"から_proptyに変更)
class D6(_propty: String) {
var propty = _propty // D6のプロパティーに_proptyを与えるが、この_proptyはこの行では何も寄与せず、
// init{...}ブロックの中の式に従って動作する。
get() = field.capitalize()
set(value) {
field = value + "def"
}
init{
propty = _propty // コンストラクタに与えられた値は、D5クラスのinit{...}と同じ動作をする。
}
constructor() : this("ghi")
}
第2章チャレンジ!答案
第3章チャレンジ!答案
第4章チャレンジ!答案
第7章チャレンジ!答案
第8章チャレンジ!答案
第10章チャレンジ!答案
第13章チャレンジ!答案
第15章チャレンジ!答案