※翻訳がわかりにくい場合は原文を読んでください。
KotlinコードはJavaから簡単に呼び出すことができます。
プロパティ
Kotlinプロパティは、次のJava要素にコンパイルされます。
get
接頭辞を前置することによって計算された名前を持つgetterメソッドset
接頭辞を前置することによって計算された名前を持つsetterメソッド(var
プロパティに対してのみ)- プロパティ名と同じ名前のプライベートフィールド(バッキングフィールドを持つプロパティのみ)
例えば、 var firstName: String
は以下のJava宣言にコンパイルされます
private String firstName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
プロパティの名前が is
で始まる場合、別の名前マッピング規則が使用されます。ゲッターの名前は次のようになります。 プロパティ名と同じで、setterの名前は is
をset
に置き換えることで得られます。 たとえば、プロパティisOpenの場合、getterはisOpen()と呼ばれ、setterはsetOpen()と呼ばれます。 このルールは、 Boolean
だけでなくあらゆるタイプのプロパティに適用されます。
パッケージレベルの関数
拡張機能を含む org.foo.bar
パッケージ内のexample.kt
ファイル内で宣言されたすべての関数とプロパティは、 org.foo.bar.ExampleKt`という名前のJavaクラスの静的メソッドにコンパイルされます。
// example.kt
package demo
class Foo
fun bar() {
}
// Java
new demo.Foo();
demo.ExampleKt.bar();
生成されたJavaクラスの名前は、 @JvmName
アノテーションを使用して変更できます。
@file:JvmName("DemoUtils")
package demo
class Foo
fun bar() {
}
// Java
new demo.Foo();
demo.DemoUtils.bar();
生成された同じJavaクラス名を持つ複数のファイルを持つ(同じパッケージで同じ名前または同じ @JvmNameアノテーション)は通常エラーです。 ただし、コンパイラには、指定された名前を持ち、その名前を持つすべてのファイルのすべての宣言が含まれる単一のJavaファサードクラスを生成する機能があります。 このようなファサードの生成を可能にするには、すべてのファイルで@JvmMultifileClassアノテーションを使用します。
// oldutils.kt
@file:JvmName("Utils")
@file:JvmMultifileClass
package demo
fun foo() {
}
// newutils.kt
@file:JvmName("Utils")
@file:JvmMultifileClass
package demo
fun bar() {
}
// Java
demo.Utils.foo();
demo.Utils.bar();
インスタンスフィールド
JavaのフィールドとしてKotlinプロパティを公開する必要がある場合は、それを @JvmField
アノテーションでアノテートする必要があります。 フィールドは、基になるプロパティと同じ可視性を持ちます。 @JvmField
にバッキングフィールドがある、プライベートでない、 open
、override
や const
修飾子を持たず、委譲されたプロパティでない場合、プロパティに注釈を付けることができます。
class C(id: String) {
@JvmField val ID = id
}
// Java
class JavaClient {
public String getID(C c) {
return c.ID;
}
}
Late-Initialized プロパティもフィールドとして公開されます。 フィールドの可視性は、 lateinit
プロパティーセッターの可視性と同じになります。
静的フィールド
名前付きオブジェクトまたはコンパニオンオブジェクトで宣言されたKotlinプロパティは、名前付きオブジェクトまたはコンパニオンオブジェクトを含むクラスの静的なバッキングフィールドを持ちます。
通常、これらのフィールドはプライベートですが、次のいずれかの方法で公開することができます。
@JvmField
アノテーションlateinit
修飾子const
修飾子
このようなプロパティに @JvmField
を付けると、プロパティ自体と同じ可視性を持つ静的フィールドになります。
class Key(val value: Int) {
companion object {
@JvmField
val COMPARATOR: Comparator<Key> = compareBy<Key> { it.value }
}
}
// Java
Key.COMPARATOR.compare(key1, key2);
// public static final field in Key class
オブジェクトまたはコンパニオンオブジェクトの late-initialized プロパティはプロパティセッターと同じ可視性を持つ静的なバッキングフィールドを持っています。
object Singleton {
lateinit var provider: Provider
}
// Java
Singleton.provider = new Provider();
// public static non-final field in Singleton class
const
でアノテーションされたプロパティ(クラス内とトップレベル)は、Javaの静的フィールドに変換されます
// file example.kt
object Obj {
const val CONST = 1
}
class C {
companion object {
const val VERSION = 9
}
}
const val MAX = 239
Javaの場合:
int c = Obj.CONST;
int d = ExampleKt.MAX;
int v = C.VERSION;
静的メソッド
前述のように、Kotlinは静的メソッドとしてパッケージレベルの関数を表します。 これらの関数に @JvmStatic
と注釈を付けると、Kotlinは名前付きオブジェクトやコンパニオンオブジェクトで定義された関数の静的メソッドを生成することもできます。 このアノテーションを使用すると、コンパイラはオブジェクトの囲むクラスに静的メソッドを生成し、オブジェクト自体にインスタンスメソッドを生成します。 例えば、
class C {
companion object {
@JvmStatic fun foo() {}
fun bar() {}
}
}
今、 foo()
はJavaでは静的ですが、 bar()
は静的ではありません
C.foo(); // works fine
C.bar(); // error: not a static method
C.Companion.foo(); // instance method remains
C.Companion.bar(); // the only way it works
名前付きオブジェクトと同じ
object Obj {
@JvmStatic fun foo() {}
fun bar() {}
}
Javaの場合:
Obj.foo(); // works fine
Obj.bar(); // error
Obj.INSTANCE.bar(); // works, a call through the singleton instance
Obj.INSTANCE.foo(); // works too
@JvmStatic
アノテーションは、オブジェクトまたはコンパニオンオブジェクトのプロパティに適用することもできます。そのため、getterメソッドとsetterメソッドは、そのオブジェクトまたはコンパニオンオブジェクトを含むクラスの静的メンバになります。
## 可視性
Kotlinの可視性は、次のようにJavaにマッピングされます。
private
メンバはprivate
メンバにコンパイルされます。private
トップレベル宣言はパッケージローカル宣言にコンパイルされます。protected
はprotected
のままです(Javaは同じパッケージ内の他のクラスから保護されたメンバーにアクセスすることができます Kotlinはそうしないので、Javaクラスはコードへのより広いアクセスを持つでしょう)。internal
宣言はJavaでpublic
になります。 internal
クラスのメンバは、誤ってJavaからそれらを使用するのを困難にし、Kotlinの規則に従ってお互いに見えない同じ署名を持つメンバのためにオーバーロードを許容するために、名前修飾を受けます。public
はpublic
のままです。
## Kクラス
時々 KClass
型のパラメータでKotlinメソッドを呼び出す必要があります。 Class
からKClass
への自動変換はありませんので、 Class <T> .kotlin
拡張プロパティと同等のものを呼び出すことで手動で行う必要があります:
kotlin.jvm.JvmClassMappingKt.getKotlinClass(MainView.class)
@JvmNameで署名の衝突を処理する
Kotlinには、バイトコードとは異なるJVM名が必要な名前付き関数があります。 最も顕著な例は型消去のために起こります。
fun List<String>.filterValid(): List<String>
fun List<Int>.filterValid(): List<Int>
これら2つの関数は、JVMのシグネチャが同じ filterValid(Ljava/util/List;)Ljava/util/List;
であるため、並行して定義することはできません。 Kotlinに同じ名前をつけたいのであれば、そのうちの1つ(または両方)に @JvmName
を付けて、別の名前を引数として指定することができます。
fun List<String>.filterValid(): List<String>
@JvmName("filterValidInt")
fun List<Int>.filterValid(): List<Int>
Kotlinからは filterValid
と同じ名前でアクセスできますが、JavaからはfilterValid
と filterValidInt
があります。
同じトリックが、 getX()
関数の横にプロパティ x
を持つ必要があるときに適用されます。
val x: Int
@JvmName("getX_prop")
get() = 15
fun getX() = 10
オーバーロード生成
通常、デフォルトのパラメータ値を持つKotlin関数を記述すると、Javaでは完全なシグネチャとしてのみ表示され、すべてのパラメータが表示されます。複数のオーバーロードをJava呼び出し側に公開する場合は、 @JvmOverloads
アノテーションを使用できます。
注釈は、コンストラクタ、静的メソッドなどにも使用できます。インタフェースで定義されたメソッドを含む抽象メソッドでは使用できません。
class Foo @JvmOverloads constructor(x: Int, y: Double = 0.0) {
@JvmOverloads fun f(a: String, b: Int = 0, c: String = "abc") {
...
}
}
デフォルト値を持つすべてのパラメータについて、これは追加のオーバーロードを1つ生成します。このオーバーロードには、このパラメータがあり、パラメータリストのその右側のすべてのパラメータが削除されます。この例では、次のものが生成されます。
// Constructors:
Foo(int x, double y)
Foo(int x)
// Methods
void f(String a, int b, String c) { }
void f(String a, int b) { }
void f(String a) { }
Secondary Constructorsで説明されているように、クラスにすべてのコンストラクタパラメータのデフォルト値がある場合は、引数なしのpublicコンストラクタが生成されます。これは、@JvmOverloads
アノテーションが指定されていなくても機能します。
チェックされた例外
上記のように、Kotlinは例外をチェックしていません。 したがって、通常、Kotlin関数のJavaシグネチャは例外がスローされたことを宣言しません。 したがって、Kotlinに次のような関数があるとします。
// example.kt
package demo
fun foo() {
throw IOException()
}
そして、Javaから呼び出して例外をキャッチしたい
// Java
try {
demo.Example.foo();
}
catch (IOException e) { // error: foo() does not declare IOException in the throws list
// ...
}
foo()
は IOException
を宣言しないので、Javaコンパイラからエラーメッセージが出ます。 この問題を回避するには、Kotlinで @Throws
アノテーションを使用してください。
@Throws(IOException::class)
fun foo() {
throw IOException()
}
Null-safety
JavaからKotlin関数を呼び出すときに、null以外のパラメータとして null{: .keyword } を渡すことができなくなります。 だからこそ、Kotlinは、非nullを期待するすべてのパブリック関数のランタイムチェックを生成します。 このようにして、Javaコードで即座に NullPointerException
を取得します。
バリアントジェネリックス
Kotlinクラスが 宣言サイトの分散 を利用する場合、 その使用法がJavaコードからどのように見えるかという2つのオプションがあります。次のクラスとそれを使用する2つの関数があるとしましょう。
class Box<out T>(val value: T)
interface Base
class Derived : Base
fun boxDerived(value: Derived): Box<Derived> = Box(value)
fun unboxBase(box: Box<Base>): Base = box.value
これらの関数をJavaに変換する素朴な方法は次のとおりです。
Box<Derived> boxDerived(Derived value) { ... }
Base unboxBase(Box<Base> box) { ... }
問題は、Kotlinでは unboxBase(boxDerived("s"))
と言うことができますが、Javaでは不可能です。なぜなら、JavaではクラスBox
はパラメータT
で不変であるため、従って Box<Derived>
は Box<Base>
のサブタイプではありません。 Javaで動作させるには、次のように unboxBase
を定義する必要があります。
Base unboxBase(Box<? extends Base> box) { ... }
ここでは、Javaのワイルドカード型(? extends Base
)を使用して、使用サイトの分散によって宣言サイトの分散をエミュレートします。なぜなら、それはJavaが持つすべてのものだからです。
Kotlin APIをJavaで動作させるために、パラメータとして現れるときに、共変に定義された Box
(反逆的に定義されたFoo
の場合は Foo<? super Bar>
)のBox<Super>
を Box<? extends Super>
として生成します。戻り値の場合、ワイルドカードは生成されません。そうしなければ、Javaクライアントは対処する必要があります(一般的なJavaコーディングスタイルに反します)。したがって、この例の関数は実際には次のように変換されます。
// return type - no wildcards
Box<Derived> boxDerived(Derived value) { ... }
// parameter - wildcards
Base unboxBase(Box<? extends Base> box) { ... }
注意:引数の型がfinalの場合、通常はワイルドカードを生成する点はないので、どの位置にあっても Box <String>
は常に Box <String>
です。
デフォルトで生成されないワイルドカードが必要な場合は、 @JvmWildcard
アノテーションを使用できます。
fun boxDerived(value: Derived): Box<@JvmWildcard Derived> = Box(value)
// is translated to
// Box boxDerived(Derived value) { ... }
一方、生成されるワイルドカードが必要ない場合は、 @ JvmSuppressWildcards
を使用することができます。
fun unboxBase(box: Box<@JvmSuppressWildcards Base>): Base = box.value
// is translated to
// Base unboxBase(Box<Base> box) { ... }
注意: @JvmSuppressWildcards
は、個々の型引数だけでなく、関数やクラスなどの宣言全体でも使用でき、その中のすべてのワイルドカードを抑止できます。
Nothing型の翻訳
型 Nothing
は、Javaに自然な対応がないため特別です。 実際、 java.lang.Void
を含むすべてのJava参照型は値としてnull
を受け取り、 Nothing
はそれを受け入れません。したがって、このタイプはJavaの世界では正確に表現できません。 これがKotlinが Nothing
型の引数が使われる生の型を生成する理由です:
fun emptyList(): List<Nothing> = listOf()
// is translated to
// List emptyList() { ... }