Kotlinの基本型(Primitive type)と参照型(Reference type)
基本型と参照型
Javaでは基本型(Primitive type)と参照型(Reference type)を区別して使用するが、KotlinではJavaとは異なり、Int、Long、Booleanなどのように見た目上は区別せずに使用する。
では、Kotlinには基本型と参照型が別々に存在しないのだろうか。nullがあることを考えると、次の2つを推測できる。
- 型に
nullがあるので、必ず参照型として宣言される。 - 型が
nullを許可するなら参照型、nullを許可しないなら基本型になる。
ここでの正解は後者である。型がnullを許可するなら参照型として宣言され、nullを許可しないなら基本型として宣言される。
つまりJVMでは、Kotlinでデータ型をどのように宣言したかによって、基本型と参照型に変換される。
ではサンプルコードで確認してみよう。
Kotlin
fun main() {
val n1 = 10
val n2: Int = 20
val n3: Int? = 30
println("n1=$n1, n2=$n2, n3=$n3")
}
上のコードでは、次の3つの場合で変数を宣言している。
Intが明示されていない場合Intとして宣言され、nullを許可しない場合Int?として宣言され、nullを許可する場合
これをバイトコードに変換して一部を確認すると、次のようになる。
Bytecode
L0
LINENUMBER 4 L0
BIPUSH 10
ISTORE 0
L1
LINENUMBER 5 L1
BIPUSH 20
ISTORE 1
L2
LINENUMBER 6 L2
BIPUSH 30
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
ASTORE 2
L0、L1ではそれぞれ10、20が代入されていることが分かり、L2では30が代入され、Integer.valueOf(int i)が呼び出されて参照型として返されていることが分かる。
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
さらに分かりやすくJavaへデコンパイルしてコードを見てみよう。
Java
public final class DataTypePrimitiveReferenceTutorialKt {
public static final void main() {
int n1 = 10;
int n2 = 20;
Integer n3 = 30;
String var3 = "n1=" + n1 + ", n2=" + n2 + ", n3=" + n3;
boolean var4 = false;
System.out.println(var3);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
}
型がJavaでは次の表のように変換されていることが分かる。
| データ型宣言 | Kotlin型 | Java型 |
|---|---|---|
var n1 = 10 |
Int |
int |
var n2: Int = 20 |
Int |
int |
var n3: Int? = 30 |
Int |
Integer |
Kotlinの型で注意すべき点
Kotlinでは基本型と参照型の区別がないというより、隠されていると考える方がよさそうである。 一般的には型を区別せずに使用しても大きな問題はないが、Kotlinからリフレクションを活用したJavaライブラリを呼び出す場合には問題が起こる可能性がある。
次のコードは実際のJavaライブラリ(spring-retry)から抜粋したコードであり、ここではParameterとArgumentの型を比較している。
if (!parameterTypes[i].isAssignableFrom(argument.getClass())) {
return false;
}
ParameterがintでArgumentがIntegerになるとfalseが返され、ユーザーが意図しない動作になる可能性がある。