Edited at

kotlinはJavaからどう見えるか?

More than 3 years have passed since last update.

kotlinはjavaとの互換性を念頭において設計されています。では、kotlinとJavaが共存している時にkotlin特有の実装と思われる機能は実際にJavaからどのように見えるのでしょうか?


プロパティ

kotlinでvalvarでプロパティの宣言をしてみます。


KotlinSample.kt

class KotlinClassA{

val a:Int = 1
var b:Int = 2
}


これがJavaでは

val a:Intで宣言したものはgetA()のgetterのみ

var b:Intで宣言したものgetB()setB(int i)とgetterとsetterの両方が生成されているようです。


get/setを使わずにプロパティアクセスしたい

kotlinのプロパティに@JvmFieldアノテーションを指定することでJavaからはget/setメソッドではなく直接プロパティとしてアクセスすることができます。


kotlin

class KotlinClassA{

@JvmField val a:Int = 1
@JvmField var b:Int = 2
}


これでJavaからはプロパティとしてアクセスすることができます。


Java

        KotlinClassA classA = new KotlinClassA();

int x = classA.a;
int y = classA.b;
classA.b = 4;


valで宣言されたプロパティはjavaとしてはfinal指定であると解釈されているため以下の記述はビルドエラーになります。


Java

        classA.a = 5; // エラー: final変数aに値を代入することはできません



トップレベルの関数

kotlinではソースファイル上に直接関数を宣言することができます。一方Javaではclass内のstaticあるいはinstanceメソッドとしてしか手続きを実装することができません。

こんな感じにKotlinSample.ktというファイルに関数の実装をしてみると


KotlinSample.kt


fun func():Int = 3


これがJavaでは


MainActivity.java


KotlinSampleKt.func();


ファイル名のKotlinSample.ktに対応するクラスKotlinSampleKtクラスが生成され、そのstaticメソッドとして実行することができます。


ファイルに対応するクラス名を変えたい

このKotlinSample.ktから由来するクラス名を変更したいときは、以下のようにファイルの先頭に記述することによって任意のクラス名に変更することができます。

※ ドキュメントには記載箇所の指定はありませんが、現状package指定よりも前の行に記述する必要があるようです。

※ また、以下の実装はビルドが通りますが、何故かAndroidStudio上ではエラーとして表示されます。


KotlinSample.kt


@file:JvmName("KotlinSampleB")
package com.github.yamamotoj.kotlin_java_sample

fun func():Int = 3


Javaでは


MainActivity.java


KotlinSampleB.func();


のように指定したクラス名で実行することができます。


kotlinで宣言したinterface

上記のようにkotlinのプロパティはget/setメソッドとしてJavaからアクセスできます。これはinterfaceの実装でも同様です。


KotlinSample.kt

interface KotlinInterface{

val a:Int
var b:Int
}

このinterfaceをJavaで実装すると?


Java

    class KotlinInterfaceImpl implements KotlinInterface{

@Override
public int getA() {
return 0;
}

@Override
public int getB() {
return 0;
}

@Override
public void setB(int i) {

}
}



interfaceにデフォルト実装がある場合は?

kotlinではinterfaceにデフォルトの実装を持たせることができます。


kotlin

interface KotlinInterface2{

val a:Int get() = 1
fun getValue():Int {
return a
}
}

このinterfaceをJavaで実装すると?


Java

    class KotlinInterface2Impl implements KotlinInterface2{

@Override
public int getA() {
return 0;
}

@Override
public int getValue() {
return 0;
}
}


普通にデフォルト実装は無視されるようですね ^^;


Companionオブジェクト

kotlinでクラスのstaticメソッドを実装するには以下のようにcompanion objectを介して実装します。


Kotlin

class KotlinClassB {

companion object{
fun funcB():Int{
return 2
}
}
}

これをJavaからアクセスすると


Java

        KotlinClassB.Companion.funcB();


のようにCompanionオブジェクトを介してstaticにアクセスが可能です。


Companionオブジェクトを介さずにstaticメソッドにアクセスする

Javaからstaticにアクセスしたいメソッドに対しては@JmvStaticアノテーションを付けることで


Kotlin

class KotlinClassB {

companion object{
@JvmStatic fun funcB():Int{
return 2
}
}
}


JavaからはCompanionオブジェクトを介さず直接メソッドにアクセスできます。


Java

        KotlinClassB.funcB();



シングルトンオブジェクト

kotlinではclass指定の代わりにobject指定をすることによりシングルトンオブジェクトの宣言をすることができます。


Kotlin

object KotlinObject{

val a:Int = 1
}

このオブジェクトはkotlinのソースからは


Kotlin

    KotlinObject.a


とアクセスできますがJavaからは


Java

        KotlinObject.INSTANCE.getA();


のようにINSTANCEというオブジェクトを介してアクセスができます。

また、companion objectの場合と同様に直接アクセスしたいメソッドに@JvmStaticアノテーションを付けることで


Kotlin

object KotlinObject {

@JvmStatic val a: Int = 1
}

Javaからは


Java

    KotlinObject.getA();


と直接アクセスすることができます。


Sealed class

sealed class


Kotlin

sealed class SealedClass {

class A : SealedClass()
class B : SealedClass()
}

Javaでは普通のインナークラスとして解釈されるようです。


Java

    SealedClass sealed = new SealedClass.A();



Extensions

kotlinではクラスのメソッド拡張の機能があり、継承を行わずとも既存のクラスに新たにメソッドを追加することができます。


Kotlin


class KotlinClassA {
}

class KotlinClassB {
fun KotlinClassA.extendClassA(): Int = 3
}


このようにKotlinClassBからKotlinClassAのメソッドを拡張した場合、JavaではKotlinClassAを引数に持つKotlinClassBのメソッドとしてアクセスできます。


Java

        KotlinClassA classA = new KotlinClassA();

KotlinClassB classB = new KotlinClassB();
classB.extendClassA(classA);

トップレベルの関数としてメソッドを拡張した場合は、


KotlinSample.kt

class KotlinClassA {

}

fun KotlinClassA.extend(): Int = 3


前出のようにファイル名に由来するクラスのstaticメソッドとして拡張関数にアクセスできます。


Java

        KotlinClassA classA = new KotlinClassA();

KotlinSampleKt.extend(classA);


Nullable

kotlinの便利な機能として?を使用したNullable指定があります。


Kotlin

class KotlinOptional {

var nullableString:String? = null
var nonNullString:String = "not null!"
}

これらはJavaからは取得する場合は代わりはありません


Java

        KotlinOptional optional = new KotlinOptional();

String nonnull = optional.getNonNullString();
String nullable = optional.getNullableString();

ただ、Nullableではない変数にnullを代入しようとするとその時点でNullPointerExceptionがthrowされます。

上記のようにStringだとJavaから見て?のある無しで違いが見られませんでしたがIntの場合は


Kotlin

class KotlinOptional2 {

var nullableInt:Int? = null
var nonNullInt:Int = 1
}

nonNullの場合はint型でnullableの場合はInteger型なんですね。へぇー。


まとめ

kotlinでの実装もJavaから問題なくアクセスできるようで、安心して導入できますね。