Kotlin Primitive Types and Reference Types

Primitive Types and Reference Types

Java distinguishes between primitive types and reference types, but in Kotlin they are used without such a visible distinction, as with Int, Long, and Boolean.

Then does Kotlin not have separate primitive and reference types? Considering that null exists, we can make two guesses.

  • Since a type can contain null, it will always be declared as a reference type.
  • If a type allows null, it will be a reference type; if it does not allow null, it will be a primitive type.

The correct answer here is the latter. If a type allows null, it is declared as a reference type, and if it does not allow null, it is declared as a primitive type.
In other words, on the JVM, Kotlin types are converted into primitive or reference types depending on how the data type was declared in Kotlin.

Let’s look at an example.
Kotlin

fun main() {
    val n1 = 10
    val n2: Int = 20
    val n3: Int? = 30

    println("n1=$n1, n2=$n2, n3=$n3")
}

The code above declares variables in the following three cases.

  • Int is not explicitly declared.
  • Declared as Int, which does not allow null.
  • Declared as Int?, which allows null.

If we convert this to bytecode and inspect part of it, it looks like the following.
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

In L0 and L1, 10 and 20 are assigned respectively. In L2, 30 is assigned and Integer.valueOf(int i) is called, returning a reference type.

    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;

Now let’s decompile it into Java and look at the code more easily.
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();
   }
}

You can see that the types were transformed in Java as shown in the following table.

Data type declaration Kotlin type Java type
var n1 = 10 Int int
var n2: Int = 20 Int int
var n3: Int? = 30 Int Integer

Notes on Kotlin Types

In Kotlin, it is probably more accurate to say that the distinction between primitive and reference types is hidden rather than absent. In general, using types without distinguishing them is usually not a major problem, but there can be issues when calling Java libraries that use reflection from Kotlin.

The code below is extracted from an actual Java library, spring-retry. It compares the types of a parameter and an argument.

if (!parameterTypes[i].isAssignableFrom(argument.getClass())) {
    return false;
}

If the parameter is int and the argument is Integer, false is returned, which can cause behavior the user did not intend.