概要
KotlinはJavaの何を改善したのかを、私がJavaのキライな所
を軸に、なぜJavaがそういう言語仕様になったかを含めて調べた結果をまとめてみました。
【前置き】
・私はJavaは好きですが、Kotlinは現時点で使った事はありません。(なので内容は薄いかもです)
・Kotlinがどんな言語か、文法等は他で多く触れられているのでここでは触れていません。
[戯言]
Javaはサーバサイドプログラマの中では最も信者が多い言語ではないでしょうか?
思想が崇高で、ビギナーを受け付けない。
途中からオブジェクト指向になった他の言語達とは違い、変な抜け道を許さない。
論理的に考える事が好きなエンジニアにとってこの言語での設計はたまりませんよね?(笑)
[調べた経緯]
ただ、そんなJavaにも欠点もあるしキライな人は沢山いる。
そして、何といってもプライベートで使いたい人が非常に少ない(泣)
という事で、私が行っている勉強会で、Javaをプライベートで使っている人がいるか確認してみた所、予想通りというか、15人中一人もJavaは使っていませんでした(大泣)
ただ、Kotlinを使っている人は3名もいたんです。
そこからKotlinについて興味を持ち調べる事にしました。
本題
まず、私にとってJavaのキライな所ですが、こんな感じです。
- Getter/Setterを全部書いていく文化
- 検査例外
※メソッドは投げる例外を明示しないといけない。
受ける方はそれを処理するか、さらに上に投げないといけない。 - NullPointerException
- プリミティブ型とオブジェクト型の混在
これらがどう改善されたのだろうか!?
1.Getter/Setterを全部書いていく文化
[なぜ?]
まず、何故JavaはGetter/Setterを全て書いて行くのかを知る必要があります。
自分はあまりはっきり理解していませんでしたが、調べると色んな意見があります。
・メンバ変数を隠蔽して安全にする為
・メンバ変数への変更を監視可能とする為(処理を挟む余地を作る)
これらではイマイチ納得できませんでしたし、publicで定義しても良いように思えていましたが、最終自分の中で辿りついた正解はこれ
・メンバ変数はサブクラスでオーバーライド出来ない!
だからメソッドでラップして、サブクラスで動作を変えたければそのメソッドをオーバーライドする。
[改善]
これは、C#やruby,swiftにあるプロパティとなりました。
なので、Getter/Setterを書いていく必要はありません。
プロパティはサブクラスでオーバーライド可能。
抽象化してサブクラスでの実装を強制する事もできます。
2. 検査例外(checked exception)
[何?]
唯一Javaでのみ取り入れられている方式らしいです。
メソッドが投げる例外はメソッドの構文の一部として明示し、呼び出し元ではそれを処理するかさらに上に投げる必要がある。
public FileInputStream(String name)
throws FileNotFoundException
これには以下のようなメリットがあります。
多くの言語や機構は例外を実行時のものとして扱っており、実際に発生するまではどのような例外が発生するのか、そもそもこの処理から例外が発生しうるのかがわかりません。特に、Javaの場合は例外の種類までわかるため、例外回復に多くの期待が持てます。
引用元:検査例外再考
ただ、これは今では過ちだったとされ、C#を代表する他のオブジェクト指向言語には取り入れられていません。
意味のないcatch句が量産され実際にはプログラムの品質を落としている。
引用元:The Scalability of Checked Exceptions
[改善]
検査例外は無くなり、呼び出し元での例外のハンドリングは強制されなくなった。
3. NullPointerException
[何?]
インスタンスが出来る前の変数を使ったら発生します。
// Javaの場合は実行時エラー
String s = null;
s.length(); // java.lang.NullPointerException
[改善]
これはどうしようもないんじゃ?と、思っていましたが、最近の言語ではこれを安全にする仕組みを備えています。
参考:null安全でない言語は、もはやレガシー言語だ
Kotlinでは、Nullを持ち得る場合は宣言時に明示し、Nullに成り得る場合は先にチェックしないとコンパイルエラーとなります。
// 変数を?付き宣言でnullが設定され得ます。?無しだとnull設定不可。
val s: String? = null
s.length // コンパイルエラー: only safe (?.) or non-null
↓
//OKな書き方
val s:String? = null
if (s != null){
s.length // OK
}
4. プリミティブ型とオブジェクト型の混在
御存知の通り、Javaには8つのプリミティブ型(boolean,char,byte,short,int,long,float,double)があり、これらにはオブジェクト型のラッパークラスがあります。
[なぜ?]
なぜプリミティブ型の変数があるのか?
JavaはC++からその多くのアイディアを取り入れています。
プリミティブ型の変数もその一つ。
そして、これを取り入れる事で既存のC++プログラマの流入を狙ったというのがしっくりきました。
他にも、プリミティブ型は処理速度が速いというメリットもあるようです。
参考元:Primitive Types Considered Harmful
[問題]
・扱っている変数がプリミティブ型かオブジェクト型か意識しながら、処理を使い分ける必要があります。
⇒【例】プリミティブ型は ==
で比較出来る所を、オブジェクト型は .equals()
を使う等。
・オブジェクト型を引数にするメソッドにプリミティブ型の変数を渡すには、一度オブジェクト型に直す必要があり、それによりメモリを無駄に使う事にもなります。
[改善]
Kotlinではプリミティブ型はありません。
(ただし、プリミティブ型が持つ手軽さは一部失ったとも言えます)
以上ですが、とりあえず、個人的にイマイチと言える部分はほぼ改善されたという印象を持ちました。
Javaをやる事があれば、Kotlin使ってみたいですね。