質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

解決済

1回答

970閲覧

Kotlinのセカンダリコンストラクタからプライマリコンストラクタの委譲とは?

tomuziso

総合スコア40

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

1グッド

0クリップ

投稿2019/02/11 15:05

https://kotlinlang.org/docs/reference/classes.html
kotlin公式のセカンダリコンストラクタのサンプルコードです。

class Person(val name: String) { constructor(name: String, parent: Person) : this(name) { parent.children.add(this) } }

上記コードの説明で委譲という言葉を使っていますが、噛み砕くとどいう意味なのでしょうか?
今回の例だと、セカンダリコンストラクタを呼んだときに
: this(name)
と、ありますが、これはプライマリコンストラクタを呼ぶための記述ということで合っていますでしょうか?
(単にセカンダリ→プライマリと順に呼んでいきインスタンスを作成する、という意味で委譲ということ・・・?)

また、parent.children.add(this)
add(this)とありますが、このthisは1つ目のthisと違いclass自体を呼んでいます。

これはどういうことでしょうか?
コンストラクタを呼んで初期化の最後の段階に自分自身のインスタンスを(this)として呼んだ ということでしょうか?

どなたか分かる方居ましたら教えていただきいたいです。

shunsukekato👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

プライマリコンストラクタが定義されているクラスでは、セカンダリコンストラクタでインスタンスを生成する場合、直接的または間接的にプライマリコンストラクタを呼び出す必要があります。

kotlin

1// Primary constructor 2class Person constructor(val name: String) { 3 // Secondary constructor (A) 4 // このコンストラクタのthis(...)はプライマリコンストラクタを呼んでいます 5 constructor(name: String, age: Int, person: Person) : this(name) { 6 person.children.add(this) 7 this.age = age 8 } 9 // Secondary constructor (B) 10 // このコンストラクタのthis(...)は上記のSecondary consutructor (A)を呼んでいます 11 constructor(name: String, person: Person) : this(name, 0, person) { 12 } 13 14 var age: Int = 0 15 val children = mutableListOf<Person>() 16 17 override fun toString(): String { 18 return "Person(name='$name', age=$age, children=$children)" 19 } 20 21}

上記のコードでいえば、Secondary constructor (A)が直接プライマリコンストラクタを呼び出しています。
また、Secondary constructor (B)ではSecondary constructor (A)を経由して間接的にプライマリコンストラクタを呼び出しています。

今回の例だと、セカンダリコンストラクタを呼んだときに
: this(name)
と、ありますが、これはプライマリコンストラクタを呼ぶための記述ということで合っていますでしょうか?

この例で言えばその認識であっていると思いますが、正確には他のコンストラクタを呼び出す記述です。(プライマリコンストラクタでなくてもいいということです)
"Kotlin","コンストラクタ"等のキーワードで検索すれば詳しく解説しているサイトが見つかると思いますのでご確認ください。

また、parent.children.add(this)
でadd(this)とありますが、このthisは1つ目のthisと違いclass自体を呼んでいます。

これはどういうことでしょうか?

このthisキーワードは自分自身のインスタンスを指すキーワードです。
コンストラクタで使われていたthisは他のコンストラクタを呼び出すthisキーワードです。
つまりthisキーワードには2通りの使い方があるということになります。

Keywords and Operatorsからthisキーワードの説明を下記に引用します。

refers to the current receiver
(現在のレシーバを参照します)
calls another constructor of the same class from a secondary constructor
(セカンダリコンストラクタから同じクラスの別のコンストラクタを呼び出す)

この処理は自分自身のインスタンス(this)を、コンストラクタ引数に渡されたPersonインスタンスのchildrenプロパティに追加している処理になります。

kotlin

1person.children.add(this)

下記のサンプルコードで確認すると分かりやすいと思います。

kotlin

1fun main(args: Array<String>) { 2 val father = Person("父") 3 val son = Person("息子", 5, father) 4 val daughter = Person("娘", father) 5 6 println(father) 7 println(son) 8 println(daughter) 9 // Person(name='父', age=0, children=[Person(name='息子', age=5, children=[]), Person(name='娘', age=0, children=[])]) 10 // Person(name='息子', age=5, children=[]) 11 // Person(name='娘', age=0, children=[]) 12 13}

2019/02/14追記

下記のコードは、回答欄に記述したクラスのプライマリコンストラクタを少し修正したものです。

class Person constructor(name: String) { // このブロック内のコードがプライマリコンストラクタ呼び出し時に実行される val name: String = name //コンストラクタの引数でプロパティを初期化 var age: Int = 0 // デフォルト値で初期化 val children= mutableListOf<Person>() // 同上 // init blockはそのあとに実行される init { println("Init Block. name=${this.name} age=${this.age} children=${this.children.size}") } }

さらに、initブロックが記述されていればプリライマリコンストラクタの実行後に実行されます。

この時点で処理の順番はインスタンス生成 → プライマリコンストラクタ → initブロック → セカンダリコンストラクタになります。

試しにinitブロックやコンストラクタ内にprint文を入れて実行してみると次のようになります。

kotlin

1class Person constructor(name: String) { 2 val name: String = name 3 var age: Int = 0 4 val children= mutableListOf<Person>() 5 6 // Init Block 7 init { 8 println("Init Block. name=${this.name} age=${this.age} children=${this.children.size}") 9 } 10 11 // Secondary constructor (A) 12 constructor(name: String, age: Int, person: Person) : this(name) { 13 println("constructor A. age=${this.age} children=${this.children.size}") 14 person.children.add(this) 15 this.age = age 16 } 17 // Secondary constructor (B) 18 constructor(name: String, person: Person) : this(name, 0, person) { 19 println("constructor B. age=${this.age} children=${this.children.size}") 20 } 21 22 override fun toString(): String { 23 return "Person(name='$name', age=$age, children=$children)" 24 } 25 26}

実行してみます。

kotlin

1val father = Person("父") 2// Init Block. name=父 age=0 children=0 3println(father) 4// Person(name='父', age=0, children=[]) 5 6val son = Person("息子", 5, father) 7// Init Block. name=息子 age=0 children=0 8// constructor A. age=0 children=0 9println(son) 10// Person(name='息子', age=5, children=[]) 11 12val daughter = Person("娘", father) 13// Init Block. name=娘 age=0 children=0 14// constructor A. age=0 children=0 15// constructor B. age=0 children=0 16println(daughter) 17// Person(name='娘', age=0, children=[])

投稿2019/02/12 14:37

編集2019/02/13 15:29
rubytomato

総合スコア1752

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tomuziso

2019/02/13 11:10

丁寧な回答ありがとうございます! > この例で言えばその認識であっていると思いますが、正確には他のコンストラクタを呼び出す記述です。> (プライマリコンストラクタでなくてもいいということです) 確かにそうでした。ここでは例としてプライマリコンストラクタを呼び出しているだけで、thisはコンストラクタを呼び出す、というのが正しいですね。 ここでコンストラクタを呼び出す、というのは間接的にプライマリコンストラクタを呼び出すことになっており、 var age: Int = 0 val children = mutableListOf<Person>() のPersonクラスのインスタンスを作成する(上記2つのプロパティが初期化される)ということですよね? つまり、 constructor(name: String, age: Int, person: Person) : this(name) { person.children.add(this) this.age = age } のpersion.children.add(this)で、自分自身のインスタンスを表すthisが使用できるのは既にインスタンス化が済んでいるから(プロパティ(children)の初期化が済んでいるから)ということで合ってますでしょうか?
rubytomato

2019/02/13 15:29 編集

> のpersion.children.add(this)で、自分自身のインスタンスを表すthisが使用できるのは既にインスタンス化が済んでいるから(プロパティ(children)の初期化が済んでいるから)ということで合ってますでしょうか? はい、私も同じ認識です。 インスタンス自体はコンストラクタが呼び出される前に生成されていて(Java的に言えばnew演算子で)、そのあとにプライマリコンストラクタでプロパティが初期化されるというふうに理解しています。 回答欄に追記しましたのでご確認ください。
tomuziso

2019/02/14 15:08

コメントありがとうございkます。 略: this(name) { person.children.add(this) this.age = age } 上記の person.children.add(this)の自分自身のインスタンスを呼ぶthisが使えるのは 既に インスタンス生成 → プライマリコンストラクタ という手順を踏んでからセカンダリコンストラクタ内の記述を実行しているため使える、ということですね。 とても分かりやすい回答を頂き勉強になりました。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問