連載
» 2018年04月02日 05時00分 公開

Android Studioで始めるKotlin入門(4):Kotlinにおけるクラス、プロパティ、コンストラクタ、データクラス、シングルトン (2/3)

[土井毅(著)/山田祥寛(監修),WINGSプロジェクト]

プライマリコンストラクタ

 続いてはコンストラクタの書き方についてです。Kotlinでは、Javaとは少し異なり、「プライマリコンストラクタ」と呼ばれるコンストラクタをclassキーワード直後に記述します。プライマリコンストラクタ以外のコンストラクタは「セカンダリコンストラクタ」と呼ばれ、「constructor」キーワードで定義します*1

*1)なお、プライマリ、セカンダリ以降の「3番目の(ターシャリ?)」コンストラクタはなく、プライマリコンストラクタ以外は全てセカンダリコンストラクタと呼ばれます。

 リスト5は姓、名のプロパティを持つPersonクラスについてプライマリコンストラクタとセカンダリコンストラクタを定義した例です。

//プライマリコンストラクタ。name引数で名のみを指定
class Person(name: String){
  //姓、名プロパティ
  var firstName: String
  var lastName: String
 
  //プライマリコンストラクタの処理内容はinitで記述
  init{
    this.firstName = name //プライマリコンストラクタの引数をプロパティに代入
    this.lastName = "" //姓は未指定とする
  }
 
  //セカンダリコンストラクタ。姓、名両方を指定
  //this()でプライマリコンストラクタを呼び出す
  constructor(firstName: String, lastName: String): this(firstName){
    this.lastName = lastName //姓に2番目の引数を代入する
  }
 
  fun print(){
    println("お名前: ${firstName} ${lastName}")
  }
}
……
//プライマリコンストラクタの呼び出し例
val person = Person("Doi")
person.print() //結果: 「お名前: Doi」
 
//セカンダリコンストラクタの呼び出し例
val person2 = Person("Tsuyoshi", "Doi")
person2.print() //結果: 「お名前: Tsuyoshi Doi」
リスト5 プライマリコンストラクタとセカンダリコンストラクタ

 Javaの書き方と雰囲気がガラッと変わったので混乱するかもしれませんが、前述の通りclassキーワードの直後がそのままプライマリコンストラクタの定義となるので、そこに引数を記述しています。プライマリコンストラクタの処理内容は「init」キーワードで別途記述します。initキーワードの中では、「firstName」プロパティにプライマリコンストラクタの「name」引数の値を設定しています。

 セカンダリコンストラクタはconstructorキーワードを使って定義します。なお、セカンダリコンストラクタの定義の後に「this(firstName)」のように書いています。これはセカンダリコンストラクタの呼び出しの際に、他のコンストラクタ(ここではプライマリコンストラクタ)を呼び出す書き方です。Javaにおいてコンストラクタの先頭でthis()を使って他のコンストラクタを呼び出すのと同じですね。

 なおKotlinにおいては、セカンダリコンストラクタから別のセカンダリコンストラクタを呼び出すことは許されますが、いずれのケースでも最終的に必ずプライマリコンストラクタが呼び出される必要があります。各コンストラクタが独立してインスタンス生成することを許すJavaとは異なることに注意してください。

 なお、コンストラクタを呼び出す際にはJavaと同様に、引数の数や型に合わせて適切なコンストラクタが呼び出されます。

プライマリコンストラクタでのプロパティ定義とデフォルト値

 さて、リスト5を見ていて「コンストラクタの引数をわざわざ代入するだけのコードはかったるいなぁ」と思いませんか? 多くのコンストラクタの役割は上記のように「プロパティの値を設定する」ものですので、Kotlinではリスト6のようにプライマリコンストラクタの中でプロパティ自体を定義できるようになっています。

//プライマリコンストラクタでプロパティを定義。デフォルト値も設定
class Person2(val firstName: String, val lastName: String = ""){
  fun print(){
    //プライマリコンストラクタで定義されたプロパティを参照
    println("お名前: ${firstName} ${lastName}")
  }
}
 
//第2引数を省略してプライマリコンストラクタを呼び出す
val person3 = Person2("Doi")
person3.print() //結果: 「お名前: Doi」
 
//第2引数を指定してプライマリコンストラクタを呼び出す
val person4 = Person2("Tsuyoshi", "Doi")
person4.print() //結果: 「お名前: Tsuyoshi Doi」
リスト6 プライマリコンストラクタでプロパティ定義とデフォルト値を設定したサンプル

 ここでは、プライマリコンストラクタで「firstName」「lastName」という引数を指定していますが、各引数の前に「val」を付けることで、この引数が自動的に書き換え不能プロパティとして扱われます。

 また、lastNameプロパティの後に代入文があることにも注目してください。これは引数省略時のデフォルト値です。この機能を使えば、引数の数が異なるだけのコンストラクタを別個に定義する必要がなくなるケースも多いでしょう。

 このプライマリコンストラクタでのプロパティ定義とデフォルト値設定により、先ほどのリスト5で20行ほどだったコードが、一気に半分以下に納まりました。圧倒的な差ですね。

コラム「Kotlinにメソッドは存在しない???」

 本連載では、今回「KotlinのクラスではJavaと同様にメソッドを定義可能」、連載第2回で「Kotlinではクラス内のメソッドとは別に、クラス外に関数として処理を書くことができる」と書いてきましたが、実はこれらは厳密に言うと正しくありません。「Kotlinにはメソッドという概念はなく、クラス内に宣言されるものも含めて全て関数である」というのがより正確な言い方になります。

 ただ「クラス内に定義された関数をメソッドと呼ぶ」のはオブジェクト指向言語プログラマーにとって非常に分かりやすいため、そのように書かれている情報源も少なくないようです。本連載でも分かりやすさを優先して、クラス内で定義された関数(Kotlinではメンバ関数とも呼ばれる)についてメソッドという表現を用いています。

 メソッドと関数は言語上同じ概念であるため、本文で説明した引数省略時のデフォルト値の設定などは、クラスのメソッドだけの特別な機能ではなく、関数においても利用可能です。今後の回でも関数のいろいろな機能について解説しますが、クラスのメソッド、関数の両方で使えることを覚えておいてください。

 ただし、コンストラクタと関数は見た目が似ているものの別物であることにも注意してください。引数にval、varを付けることでプロパティを定義できるのはコンストラクタ専用の機能で、関数では使用できません。


Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。