kotlinはjavaとの互換性を念頭において設計されています。では、kotlinとJavaが共存している時にkotlin特有の実装と思われる機能は実際にJavaからどのように見えるのでしょうか?
プロパティ
kotlinでval
やvar
でプロパティの宣言をしてみます。
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メソッドではなく直接プロパティとしてアクセスすることができます。
class KotlinClassA{
@JvmField val a:Int = 1
@JvmField var b:Int = 2
}
これでJavaからはプロパティとしてアクセスすることができます。
KotlinClassA classA = new KotlinClassA();
int x = classA.a;
int y = classA.b;
classA.b = 4;
val
で宣言されたプロパティはjavaとしてはfinal指定であると解釈されているため以下の記述はビルドエラーになります。
classA.a = 5; // エラー: final変数aに値を代入することはできません
トップレベルの関数
kotlinではソースファイル上に直接関数を宣言することができます。一方Javaではclass内のstaticあるいはinstanceメソッドとしてしか手続きを実装することができません。
こんな感じにKotlinSample.kt
というファイルに関数の実装をしてみると
fun func():Int = 3
これがJavaでは
KotlinSampleKt.func();
ファイル名のKotlinSample.kt
に対応するクラスKotlinSampleKt
クラスが生成され、そのstaticメソッドとして実行することができます。
ファイルに対応するクラス名を変えたい
このKotlinSample.ktから由来するクラス名を変更したいときは、以下のようにファイルの先頭に記述することによって任意のクラス名に変更することができます。
※ ドキュメントには記載箇所の指定はありませんが、現状package指定よりも前の行に記述する必要があるようです。
※ また、以下の実装はビルドが通りますが、何故かAndroidStudio上ではエラーとして表示されます。
@file:JvmName("KotlinSampleB")
package com.github.yamamotoj.kotlin_java_sample
fun func():Int = 3
Javaでは
KotlinSampleB.func();
のように指定したクラス名で実行することができます。
kotlinで宣言したinterface
上記のようにkotlinのプロパティはget/setメソッドとしてJavaからアクセスできます。これはinterfaceの実装でも同様です。
interface KotlinInterface{
val a:Int
var b:Int
}
このinterfaceを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にデフォルトの実装を持たせることができます。
interface KotlinInterface2{
val a:Int get() = 1
fun getValue():Int {
return a
}
}
このinterfaceをJavaで実装すると?
class KotlinInterface2Impl implements KotlinInterface2{
@Override
public int getA() {
return 0;
}
@Override
public int getValue() {
return 0;
}
}
普通にデフォルト実装は無視されるようですね ^^;
Companionオブジェクト
kotlinでクラスのstaticメソッドを実装するには以下のようにcompanion object
を介して実装します。
class KotlinClassB {
companion object{
fun funcB():Int{
return 2
}
}
}
これをJavaからアクセスすると
KotlinClassB.Companion.funcB();
のようにCompanion
オブジェクトを介してstaticにアクセスが可能です。
Companion
オブジェクトを介さずにstaticメソッドにアクセスする
Javaからstaticにアクセスしたいメソッドに対しては@JmvStatic
アノテーションを付けることで
class KotlinClassB {
companion object{
@JvmStatic fun funcB():Int{
return 2
}
}
}
JavaからはCompanion
オブジェクトを介さず直接メソッドにアクセスできます。
KotlinClassB.funcB();
シングルトンオブジェクト
kotlinではclass
指定の代わりにobject
指定をすることによりシングルトンオブジェクトの宣言をすることができます。
object KotlinObject{
val a:Int = 1
}
このオブジェクトはkotlinのソースからは
KotlinObject.a
とアクセスできますがJavaからは
KotlinObject.INSTANCE.getA();
のようにINSTANCE
というオブジェクトを介してアクセスができます。
また、companion object
の場合と同様に直接アクセスしたいメソッドに@JvmStatic
アノテーションを付けることで
object KotlinObject {
@JvmStatic val a: Int = 1
}
Javaからは
KotlinObject.getA();
と直接アクセスすることができます。
Sealed class
sealed class
は
sealed class SealedClass {
class A : SealedClass()
class B : SealedClass()
}
Javaでは普通のインナークラスとして解釈されるようです。
SealedClass sealed = new SealedClass.A();
Extensions
kotlinではクラスのメソッド拡張の機能があり、継承を行わずとも既存のクラスに新たにメソッドを追加することができます。
class KotlinClassA {
}
class KotlinClassB {
fun KotlinClassA.extendClassA(): Int = 3
}
このようにKotlinClassB
からKotlinClassA
のメソッドを拡張した場合、JavaではKotlinClassA
を引数に持つKotlinClassB
のメソッドとしてアクセスできます。
KotlinClassA classA = new KotlinClassA();
KotlinClassB classB = new KotlinClassB();
classB.extendClassA(classA);
トップレベルの関数としてメソッドを拡張した場合は、
class KotlinClassA {
}
fun KotlinClassA.extend(): Int = 3
前出のようにファイル名に由来するクラスのstaticメソッドとして拡張関数にアクセスできます。
KotlinClassA classA = new KotlinClassA();
KotlinSampleKt.extend(classA);
Nullable
kotlinの便利な機能として?
を使用したNullable指定があります。
class KotlinOptional {
var nullableString:String? = null
var nonNullString:String = "not null!"
}
これらはJavaからは取得する場合は代わりはありません
KotlinOptional optional = new KotlinOptional();
String nonnull = optional.getNonNullString();
String nullable = optional.getNullableString();
ただ、Nullableではない変数にnullを代入しようとするとその時点でNullPointerException
がthrowされます。
上記のようにStringだとJavaから見て?
のある無しで違いが見られませんでしたがIntの場合は
class KotlinOptional2 {
var nullableInt:Int? = null
var nonNullInt:Int = 1
}
nonNullの場合はint
型でnullableの場合はInteger
型なんですね。へぇー。
まとめ
kotlinでの実装もJavaから問題なくアクセスできるようで、安心して導入できますね。