Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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変換は有効になりません

yumemi
みんなが知ってるあのサービス、実はゆめみが作ってます。スマホアプリ/Webサービスの企画・UX/UI設計、開発運用。Swift, Kotlin, PHP, Vue.js, React.js, Node.js, AWS等エンジニア・クリエイターの会社です。Twitterで情報配信中https://twitter.com/yumemiinc
http://www.yumemi.co.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away