22日目:Javaとの相互運用
3連休初日キタ━(・∀・)━!!!
アドベントカレンダー初日にお話ししましたが、KotlinはJVM言語なのでコンパイラが吐くのはJavaバイトコードです。それらはJava言語から使用することができます。またその逆に、Java言語で書かれたクラスをKotlinから使用することもできます。今日はKotlinとJavaの相互運用についてお話しします。
KotlinからJavaコードを使う
Java→Kotlin
同じオブジェクト指向言語として、やはりJavaとKotlinには類似点が多くあります。例えばJavaの void はKotlinの Unit にそのまま対応しています。このような対応関係をまずは把握しましょう。
Javaコード | Kotlinではこうなる |
---|---|
返値型がvoidのメソッド | 返値型がUnitの関数 |
Kotlinの予約語と同一の識別子 | 使用する際はバッククオートで括る(`is`など) |
参照型(例えばInteger) | NULL許容型(例えばInt?) |
チェック例外 | 非チェック例外(捕捉の義務はない) |
List<? extends Hoge> List<? super Hoge> |
型プロジェクション(使用箇所宣言) List<out Hoge> List<in Hoge> |
raw型(原型) List |
スタープロジェクション List<*> つまり List<out Any?> |
java.lang.Object | Any? |
java.lang.Objectが提供するメソッド群
Kotlinでは、すべてのクラスのスーパクラスは Any です。上記の表のようにJavaコードにおけるObject型はKotlinで扱う際にAny?型に変換されます。しかしクラス Any にはいかなるメンバも定義されていません。それでは Object の提供するメソッドはどのようにKotlin側で扱えばよいのでしょうか。
メソッド toString とequals、finalize、clone()は、プログラマ視点では特に違いはありません。Kotlinがどのような仕掛けでそれらのメソッドを呼び出しているか気になる方は、Kotlinのソースコードを読んでみてください。メソッド hashCode、wait、notify、getClassは、Javaコードで定義されたクラスのみが持ちます。wait、notifyがKotlinで廃止されていることはいいことです*1。
JavaからKotlinコードを使う
名前空間レベルの関数
KotlinではJavaと異なり名前空間レベルに関数を置くことができます。実はコンパイルすると、namespaceという名前のクラスが自動生成され、そのクラスが持つ staticメソッドとなります。
例外
Kotlinにはチェック例外はありません。すべて非チェック例外です。これは単にjava.lang.RuntimeExceptionかそのサブクラスであるということではなく、文字通り非チェック例外なのです。
KotlinではJavaと同じように例外を投げることができますが、関数にスロー宣言はありません。例えば次のようなKotlinコードがあるとします。
fun throwIOException() { throw java.io.IOException() } fun main(args : Array<String>) { try { throwIOException() } catch(e : java.io.IOException) { e.printStackTrace() } }
このコードはコンパイル可能です。実行するとスタックトレースが表示されるだけのプログラムです。
上記の関数 throwIOException をJavaコードから呼び出して、上記の main と同じようなコードを書いてみます。
// Java public class Sample { public static void main(String[] args) { try { namespace.throwIOException(); } catch(java.io.IOException e) { e.printStackTrace(); } } }
「名前空間レベルの関数」で説明したように、関数 throwIOException はクラス namespace 内のstatic関数となるので、同じパッケージ内にいれば例のJavaコードのように呼び出すことができます。しかし、このコードはコンパイルに失敗します。問題の箇所は、catch節です。try節からIOExceptionはスローされ得ないという解釈がされるからです。繰り返しになりますが、Kotlinの関数はスロー宣言ができないので、このようなことが起こるのです。
この問題に対してKotlinプロジェクトチームは次のような解決策を挙げています。
- スロー宣言だけで、何も仕事をしないメソッドを定義し、それとセットで使う
- Throwableで補足してinstanceofチェックをする。あまりエレガントじゃない
- Javaでラッパメソッドを書く
- Kotlin側の関数に、アノテーションthrowsを用いてスロー宣言を行う(現時点では実装されてなさそう...)
プロパティ
プロパティには、setterとgetterを使ってアクセスします。
class Person() { var name : String = "" }
// Java final Person taro = new Person(); taro.setName("Taro"); System.out.println(taro.getName()); // Taro
まとめと次回予告
今日はKotlinとJavaとの相互運用についてお話ししました。両者の違いと、その差異を吸収する仕組みを知ることが相互運用の場面では重要になると思います。もっとも、KotlinコードをJavaから使用することはあまりないかも知れませんが。
明日はJavaのユニットテスト・フレームワークとして名高いJUnitと、Kotlinの標準ライブラリを使ったユニットテストの書き方を紹介します。
日記
東京では、クリスマスイブに雪が降るらしいですね。