Posted at

Java開発者に送るKotlinとの相互運用Tips


KotlinコードをJavaから呼び出すとき


デフォルト引数

Kotlinのデフォルト引数付き関数をJavaから扱いたい場合、全ての引数を指定しなければなりません。

ですが、関数に@JvmOverloadアノテーションを付加すると自動的にオーバーロードメソッドが生成されます。

// Kotlin

class OverloadSample {
@JvmOverloads // アノテーションをつけると、Javaから呼び出し可能なオーバーロードメソッドが生成される
fun test(arg1: Boolean = true, arg2: Int = 1, arg3: String = "true") : Boolean {
return arg1 && arg2 == 1 && arg3 == "true"
}
}

Javaからの呼び出しコードを実装してみます。

// Java

OverloadSample os = new OverloadSample();
os.test(true, 1, "true");
os.test(true, 1);
os.test(true);


トップレベル関数、プロパティ

クラスに属さないトップレベルのKotlin関数をJavaから呼び出す方法です。

Kotlinのトップレベル関数は

ソースファイル名+Ktという名前のクラスが生成され、

そのクラスのstatic関数へコンパイルされます。

// Utility.kt

fun doSomething() {
// Do something
}

// Java

UtilityKt.doSomething();

上記のソースファイル名+Ktというクラス名を変更したい場合は@JvmNameアノテーションを使います。このアノテーションはパッケージに記述しまし。

// Utility.kt

@file: JvmName("Util")
package com.sample

fun doSomething() {
// Do something
}

// Java

Util.doSomething();


拡張関数

拡張関数はトップレベル関数と同じく、ソースファイル名+Ktという名前のクラスのstatic関数としてコンパイルされます。Javaから呼び出す場合は、thisとなるオブジェクトが第一引数となります。

// StringUtility.kt

fun String.join(otherString: String) : String {
return this + otherString
}

// Java

StringUtilityKt.join("Hello, ", "World!");


objectクラス

objectクラスをJavaからみると、INSTANCEというstaticフィールドを持ったクラスとなります。objectクラスのプロパティか関数にアクセスするには、このINSTANCEを経由します。

// Kotlin

object LogUtil {
fun debug(log: String) {
//
}
}

// Java

LogUtil.INSTANCE.debug("completed!");


JavaコードをKotlinから呼び出すとき


プラットフォーム型

Java関数の戻り値をKotlinコードで受ける場合など、

その戻り値がnull許容型か否かは@NonNull@Nullable

アノテーションで判断されます。

// Java

public class Sample {

@NonNull
String getString1() {
return //
}

@Nullable
String getString2() {
return //
}
}

Kotlinで呼び出すと以下のようになります。

// Kotlin

val sample = Sample()
val str1 = sample.string1 // 非null許容型
val str2 = sample.string2 // null許容型

ただし、@NonNull@Nullableのアノテーションは

コンパイル時に強制力を持つものではないので注意が必要です。

では、アノテーションをつけない場合は...

// Java

public class Sample {
// アノテーション無し
String getString3() {
return //
}
}

Kotlinから呼び出してみます。

// Kotlin

val sample = Sample()
val str3 = sample.string3 // String!

String!という型になります。

この!のついた型をプラットフォーム型といいます。

null許容型でも非null許容型でも扱える、どっちも型です。

Nullであるか否かを開発者に委ねる考え方です。

// Kotlin

val sample = Sample()
val str = sample.string3
val length = str.length // 非null許容型として扱う
val char = str?.get(0) // null許容型として扱う


static関数、staticフィールド

何らかの理由でstatic関数やフィールドが必要な状況があり得ます。

例えば...

Kotlinでstaticっぽく使えるcompanion object

ここでアノテーションを使うと、Javaのstaticとしてコンパイルされます。

// Kotlin

class Sample {
companion object {

@JvmField // staticフィールドへのコンパイルを指定するアノテーション
var TAG: String = "Sample"

@JvmStatic // static関数へのコンパイルを指定するアノテーション
fun tag(): String {
return "Sample"
}
}
}

// Java

String tag = Sample.TAG;
tag = Sample.tag();


SAM変換

特定の条件下において、

Javaのインターフェースを実装したクラスをラムダ式で作ることができます。

val executor: ThreadPoolExecutor

// executeメソッドの引数にはRunnableを実装したオブジェクトを渡す
executor.execute(object: Runnable{
override fun run() {
//
}
})

// SAM変換により、ラムダ式で表現可能!
executor.execute {
//
}

KotlinでSAM変換が有効となる条件は以下です。


  • Javaで宣言されたインターフェースであること


  • 抽象メソッドが1つだけのインターフェースであること

Kotlinには適切な関数型が存在するのでKotlinで宣言したインターフェースではSAM変換は有効になりません