#データクラスとは
kotlin(コトリン)にはデータクラスというものがあります。これは処理は行わないけれどもデータだけをもつクラスです。
コトリンと同じJVM言語でかつ関数型プログラミング言語であるScala(スカラ)にはケースクラス(Case Class)と呼ばれるものがあります。コトリンのデータクラスはこのスカラのケースクラスとほぼ同じものだと言っていいと思います。
※実際、Kotlinの言語仕様の策定にあたっては、Scalaの言語仕様が参考にされたと思われる部分がかなりあります。
##プロパティについて
コトリンのデータクラスでは、データはクラスのプロパティとしてもちます。データクラスをインスタンス時に受け取るためのプライマリコンストラクタやプロパティの設定などのクラス内の処理は不要です。
##DTO(データ転送オブジェクト)
このデータクラスをうまく使うとデザインパターンのDTO(データ転送オブジェクト)が簡単に実装可能です。このDTOはJavaだと結構冗長な記述が必要になります。
具体的には、まず、プライベートなプロパティの宣言が必要になります。さらにデフォルトコンストラクタを定義し、デフォルトコンストラクタの引数に与えられた値をプライベートなプロパティに代入する処理が必要になります。また、プロパティのゲッター(getter)を記述しないといけません。
コトリンでは言語仕様でプライマリコンストラクタの定義は不要です。また、プロパティのゲッターの定義も不要です。ですので、データクラスの定義をサクッと一行で実装が可能となります。
#データクラスの基本書式
コトリンのデータクラスの基本書式は以下のようになります。まず date class キーワードに続いてクラス名を記述します。
クラス名の後ろの括弧 () 内にはプライマリーコンストラクタ引数を記述します。ここで、引数は不変変数キーワード val または可変変数キーワード var に続いてプロパティ名、コロン : 、型名を記述します。引数が複数ある場合には、カンマ , で区切って記述します。
また、データクラスのインスタンス化を行うには クラス名 に続いて括弧 () 内にプロパティ値をカンマ , で区切って記述します。
data class クラス名( val または var プロパティ名: 型名, ・・・・・ )
val または var インスタンス変数 = クラス名( プロパティ値, ・・・・・ )
##データクラスのコード例
コトリンのデータクラスの具体例としてデータクラス 書籍() を以下に示します。
data class 書籍( val タイトル: String, val 著者名: String, val 価格: Int )
fun main( args: Array<String> ) {
val コトリンことはじめ = 書籍( "kotlinことはじめ", "平尾菜々花", 1800 )
val ( タイトル, 著者名, 価格 ) = コトリンことはじめ
println( "◇タイトル:$タイトル ◇著者名:$著者名 ◇価格:$価格 円" )
}
###引数の定義
プライマリーコンストラクタの引数として変更不可な タイトル 、 著者名 、 価格 を定義しています。
データクラスは処理を持たないのでメソッドの記述はありません。またコトリンのクラスはプライマリコンストラクタの定義やプロパティの定義が不要です。ですので、たったこれだけでデータクラスの定義は終わりです。
###関数 main() の定義
関数 main() 内ではこのデータクラス 書籍() のプライマリコンストラクタの引数を与えてインスタンス化しています。インスタンス化したものを不変なインスタンス変数 コトリンことはじめ に代入しています。
そうして、このインスタンス変数をまとめて ( タイトル, 著者名, 価格 ) の形式で代入しています。この複数の変数をまとめて記述することを 多重定義 といいます。
ここで、データクラス 書籍() のプライマリコンストラクタの引数の順番に変数 タイトル 、 著者名 、 価格 をそれぞれ指定して多重定義としています。
####多重定義とは
なお、多重定義とは複数の変数をひとまとめにして宣言し、それぞれの変数の値をまとめて一度に定義するためのデータ記述の方法です。括弧 () 内にカンマ , 区切りで格納する値に対する変数名を複数定義します。この機能はコトリンの旧バージョンでは タプル で実現していました。 タプル はコトリンと同じJVM言語である Scala などでも使われている機能です。
これで、データクラス 書籍() のプロパティ "kotlinことはじめ" 、 "平尾菜々花" 、 1800 がそれぞれ、タプルの変数 タイトル 、 著者名 、 価格 に代入されます。
最後にデータクラスのプロパティを println() 関数内で $ 記号で文字列内に補間してコンソールに表示しています。
◇タイトル:kotlinことはじめ ◇著者名:平尾菜々花 ◇価格:1800 円
##Javaならどうなるか?
ここで、コトリンのデータクラスはJavaならどのような実装になるかを見てみましょう。
###コトリンのデータクラス 書籍() と同じ処理を行うJavaのコード例
上記のコトリンのデータクラス 書籍() と同じ処理をJavaで実装すると以下の Book.java と UsingBook.java のようになります。
まず、privateなプロパティ タイトル 、 著者名 、 価格 を宣言しています。
次に、デフォルトコンストラクタ Book() を定義しています。この Book() の処理としてprivateなプロパティへのデフォルトコンストラクタの引数を代入する処理を記述しています。
さらに、privateなプロパティ タイトル 、 著者名 、 価格 に対するゲッターをそれぞれ、メソッド getタイトル() 、 get著者名() 、 get価格() で定義しています。
コトリンのデータクラス 書籍() ではたったの1行の記述が19行にわたって記述が必要となっています。このようなデータクラスはJavaの場合にはフレームワークや統合開発環境のIDEが自動生成してくれることが多いです。しかし、この20倍近いコードを維持管理・メンテナンスするコストもバカにならないのではないでしょうか?。
public class Book {
private String タイトル;
private String 著者名;
private int 価格;
public Book(String タイトル, String 著者名, int 価格) {
this.タイトル = タイトル;
this.著者名 = 著者名;
this.価格 = 価格;
}
public String getタイトル() {
return タイトル;
}
public String get著者名() {
return 著者名;
}
public int get価格() {
return 価格;
}
}
class UsingBook {
public static void main (String[] args) throws java.lang.Exception {
Book コトリンことはじめ = new Book( "kotlinことはじめ", "平尾菜々花", 1800 );
String タイトル = コトリンことはじめ.タイトル;
String 著者名 = コトリンことはじめ.著者名;
int 価格 = コトリンことはじめ.価格;
System.out.println(
"◇タイトル:" + タイトル + " ◇著者名:" + 著者名 + " ◇価格:" + 価格 +" 円" );
}
}
◇タイトル:kotlinことはじめ ◇著者名:平尾菜々花 ◇価格:1800 円
#デフォルトプロパティ値の定義
コトリンのデータクラスのプロパティにはデフォルト値をあらかじめ定義することができます。デフォルト値を定義した場合には、データクラスのインスタンス時に省略することが可能です。
ただし、省略したことが明確になるように、インスタンス時に省略せずに指定するプライマリコンストラクタの引数はプロパティ名を明示し、 プロパティ名 = プロパティ値 のように記述します
data class クラス名( val または var プロパティ名: 型名 = デフォルト値, ・・・・・ )
val または var インスタンス変数 = クラス名( プロパティ名 = プロパティ値, ・・・・・ )
#デフォルトプロパティ値の定義の具体例
デフォルトプロパティ値を定義した具体例は以下のデータクラス 書籍 のようになります。
プロパティ タイトル のデフォルト値として "はじめてのコトリン" を定義しています。他のプロパティはデフォルト値を定義していません。
このような場合には、プロパティ タイトル はデータクラス 書籍 のインスタンス化時に省略することが可能です。
関数 main() 内でデータクラス 書籍 をインスタンス化する際に省略しています。このさい、省略したプロパティ名がわかるように、省略せずにプロパティ値を与える引数のプロパティ名を明示します。
コード例では省略されたプロパティ タイトル 以外の 著者名 と 価格 について、 著者名 = "平尾菜々花" 、 価格 = 2180 のようにプロパティ名を明示してプロパティ値を設定しています。
data class 書籍( val タイトル: String = "はじめてのコトリン", val 著者名: String, val 価格: Int )
fun main( args: Array<String> ) {
val コトリン本 = 書籍( 著者名 = "平尾菜々花", 価格 = 2180 )
val ( タイトル, 著者名, 価格 ) = コトリン本
println( "◇タイトル:$タイトル ◇著者名:$著者名 ◇価格:$価格 円" )
}
◇タイトル:はじめてのコトリン ◇著者名:平尾菜々花 ◇価格:2180 円
最後に
この記事では日本人の可読性のために関数名、クラス名、変数名などをあえて日本語にしてみました。UTF-8エンコードで問題なくコンパイルし、実行することができます。
こうすることで、コメント行をわざわざ日本語で入力する必要が減るのではないでしょうか?。私は個人的にコメント行を日本語で入力する労力をコードの実装を日本語で行ってもいいのでは?と個人的に考えていたりします。
好き嫌いはあると思いますので、実プロジェクトや関わっている案件によって、英語表記に変えるなど柔軟に対応してください。
追記 ー 日本語プログラミングに対する私の考え
私はプログラマ暦数十年のじいさまです💦。ですので、現場の事務作業を効率化するための「みんな大好き(笑)」エクセルマクロも結構な本数をVBAで作成してきました。
このおそらく100本近いマクロのVBAプログラミングにおいて、日本語のコメントは今まで一度も一切入れていません。その代わりに、関数、サブルーチン、モジュール、クラス、構造体、変数などを全て対象の業務で使う日本語で記述しています。
全世界が相手のスケールさせたいWebサービスやWebアプリ、あるいは、スマートフォンネィティブアプリなら日本語はまずいでしょう。でも、日本でしか使わない、日本人のプログラマしかメンテナンスしないことが明らかな場合にはUTF-8エンコードで安全に日本語でのプログラミングもありなのでは無いか?。そんな風に勝手に思っています。