経緯
本記事でいう安全とは
プログラミング言語の安全性は色んな観点があると思いますが、本記事では「意図しない挙動(=バグ)を生みにくい言語仕様になっていること」を指します。
Kotlinは2つの点でJavaより安全になっていると思います。
- NullPointerExceptionが起きにくい(それでも起こした人がいるらしい)
- クラスを入れ子にした際にメモリーリークが起きにくい
NullPointerException
Nullとは何か
* Javaの参照型を想定して話を進めますが、他の言語でも大枠は一緒かと思います。
* Nullが何か知っている人も、メモリーリークの話につながるのでざっと読んでほしい
String greeting = "HELLO";
と変数宣言をした際のメモリのイメージです。AA00~05までがメモリのアドレスです。
アドレスというのは、平たく言うとメモリの中のどの位置なのかという住所みたいなものですね。
このようにあるアドレスが別のアドレスに紐づいている状態を、参照していると言います。
この例だと、AA00というアドレスがAA01というアドレスから始まる要素数5の配列を参照しています。
これに1行付け足して
String greeting = "HELLO";
greeting = null;
とした状態のイメージがこちらです。(この例では0000となっている値がどうなるかはJVM(Javaの実行環境)の種類によって違います。)
AA00と具体的に紐づくアドレスがなくなりました。このように紐づいている値がないことをNullを参照していると言います。Nullを参照しているのにgreetingのプロパティを読み込んだりメソッドを呼び出したりしても、紐づいている値がないのでNullPointerExceptionという例外がでてプログラムが強制終了してしまいます。
Null-safty
KotlinがNullPointerExceptionを防ぐ仕組みは単純で、nullを代入できる型とできない型を分けるだけです。前者のオブジェクトに対しては、nullでないことを確認するまで、プロパティを読み込んだりメソッドを呼び出したり出来ません。
val nullableString: String? = "HELLO"
println(nullableString.length) //コンパイルエラー
println(nullableString.toUpperCase()) //コンパイルエラー
if (nullableString != null) {
//String?(nullかもしれない文字列型)からString(nullではない文字列型)に勝手に変えてくれる
println(nullableString.length) //コンパイルエラーにならない
println(nullableString.toUpperCase()) //コンパイルエラーにならない
}
人間ではなくプログラムがNullPointerExceptionがないか確認してくれるので安心ですね(100%じゃありませんが人がいちいち目で確認するより確実なのは確か)
クラスを入れ子にした際にメモリーリークが起きにくい
メモリーリークとは
そもそもメモリーリークは何かというと、とても恐ろしいバグです(経験有)
変数宣言なりなんなりでメモリを使った後は、「もう使わないから新しい作業に使えるよ」というのを、OSさんに教えてあげる必要があります。教えてあげるのを忘れると、メモリが足りなくなりプログラムが強制終了してしまいます。これをメモリーリークと言います。
長時間プログラムを動かさないと再現しないので、自分で動かしたり自動テストをするだけだとまず気づきません。システムテストなど全体を通して動かすテストを行う段階(つまり納期にかなり近くなってから)発覚します。メモリを計測して、どこで解放もれが起きているのかを特定するだけでも大変ですが、改修をかけて再テストを行い、解消されているのを確認するのも大変です。
ガベージコレクション
Javaなど最近の言語では、この恐ろしいバグが出ないようガベージコレクションという仕組みを採っています。どういう仕組かというと、他の箇所から参照されていないメモリは、OSが自動的に解放してくれます。
上記の例で言うと、
String greeting = "HELLO"; ←今ここ
greeting = null;
の段階では、AA01~AA05はAA00から参照されています。使っている最中なので、まだ解放できません。
String greeting = "HELLO";
greeting = null; ←今ここ
の段階では、AA00はもうAA01~AA05を参照していません。用済みなので解放してもいいですね。
JVMが勝手に解放してくれます。
このように、勝手に用済みのメモリを開放する仕組みをガベージコレクションと言います。
意図しない参照によるメモリーリーク
ガベージコレクションを行うJavaでも、メモリーリークが起きることがあります。プログラマが意図していない参照が残ってしまっている場合です。
その様な場合の一例として、以下のようなクラスがあったとします。
public class Main {
public void doSomething() {
// なんか処理をする
}
public class Sub {
public void doSomethingElse(){
// なんか違う処理をする
}
}
}
JavaではSubはMainに関係ない処理をしていたとしてもデフォルトでMainを参照します。Javaプログラマにとっては常識ですが、うっかり見逃してしまうと、うっかりですまないメモリーリークというバグの原因になります。
Kotlinで同じようなクラスを書くと、SubがデフォルトではMainを参照しないので、そもそものうっかりの原因がなくなります。(もちろん意図的にMainをSubから参照するようなコードを書けばメモリーリークの要因になります。)
以上!