Bahasa seperti Java membedakan tipe primitif (int, double, boolean) dari tipe reference (Integer, Double, Boolean). Kotlin tidak menampilkan perbedaan ini di kode — semua tipe diperlakukan sebagai objek. Namun, kompilator Kotlin tetap memanfaatkan primitive types JVM untuk efisiensi memori dan kecepatan pada situasi tertentu. Pada chapter ini kita akan membedah kapan kompilator memakai primitive, kapan memakai boxed reference, serta dampak nullabilitas terhadap representasi internal.

Kotlin tidak punya primitive types di kode #

Di Kotlin, kita tidak menulis int atau Integer — kita cukup menulis Int. Kompilator memilih representasi JVM yang paling efisien di balik layar. Saat Int digunakan dalam konteks non-nullable, kompilator biasanya menerjemahkannya ke primitive int JVM; saat digunakan sebagai nullable Int?, kompilator memakai boxed java.lang.Integer.

kotlin Tipe Kotlin dan padanannya di JVM
val a: Int = 42            // JVM: int (primitive)
val b: Int? = 42           // JVM: java.lang.Integer (boxed)
val c: Long = 42L          // JVM: long (primitive)
val d: Double = 42.0       // JVM: double (primitive)
val e: String = "halo"     // JVM: java.lang.String (reference)

Demonstrasi representasi JVM #

Berikut program yang memperlihatkan perbedaan kelas runtime antara tipe non-nullable dan nullable:

kotlin
fun main() {
    val a: Int = 42
    val b: Int? = 42
    val c: Long = 42L
    val d: Double = 42.0
    val e: String = "halo"

    println("a (Int)    = $a   -> ${a.javaClass.name}")
    println("b (Int?)   = $b   -> ${b?.javaClass?.name}")
    println("c (Long)   = $c   -> ${c.javaClass.name}")
    println("d (Double) = $d   -> ${d.javaClass.name}")
    println("e (String) = $e   -> ${e.javaClass.name}")

    val daftarNonNull: List<Int> = listOf(1, 2, 3)
    val daftarNullable: List<Int?> = listOf(1, null, 3)
    println("List<Int>  -> ${daftarNonNull.javaClass.name}")
    println("List<Int?> -> ${daftarNullable.javaClass.name}")

    val jumlahNonNull = daftarNonNull.sum()
    val jumlahNullable = daftarNullable.filterNotNull().sum()
    println("Jumlah non-null : $jumlahNonNull")
    println("Jumlah nullable: $jumlahNullable")
}

Output yang Dihasilkan:

text
a (Int)    = 42   -> int
b (Int?)   = 42   -> java.lang.Integer
c (Long)   = 42   -> long
d (Double) = 42.0   -> double
e (String) = halo   -> java.lang.String
List<Int>  -> java.util.Arrays$ArrayList
List<Int?> -> java.util.Arrays$ArrayList
Jumlah non-null : 6
Jumlah nullable: 4

Mari kita amati baris-baris penting pada kode di atas:

  1. Baris 2 (val a: Int = 42) — a.javaClass.name menghasilkan int (huruf kecil) karena kompilator memakai primitive JVM.
  2. Baris 3 (val b: Int? = 42) — karena b nullable, kompilator memakai java.lang.Integer (boxed). Property .javaClass dipanggil dengan ?. karena receiver nullable.
  3. Baris 4-5 (val c: Long = 42L, val d: Double = 42.0) — Long dan Double non-nullable dipetakan ke primitive long dan double.
  4. Baris 6 (val e: String = "halo") — String adalah tipe reference di JVM, tidak ada bentuk primitifnya.
  5. Baris 8-9 (List<Int> vs List<Int?>) — elemen List<Int> disimpan sebagai primitive int[] di balik layar; elemen List<Int?> harus disimpan sebagai Integer[] karena bisa null.
  6. Baris 11-12 (daftarNonNull.sum() vs daftarNullable.filterNotNull().sum()) — List<Int?> memerlukan .filterNotNull() sebelum operasi numerik karena elemen nullable tidak bisa langsung dijumlahkan.

Kapan primitive dipakai #

Kompilator Kotlin memakai primitive JVM ketika semua kondisi berikut terpenuhi:

  • Tipe non-nullable (Int, bukan Int?).
  • Tipe bukan tipe generik (bukan parameter type T).
  • Variabel lokal, field pada class, atau parameter function.

Jika salah satu kondisi tidak terpenuhi (misalnya variabel nullable, atau disimpan dalam List<Int?>), kompilator memakai boxed reference.

kotlin Perbandingan primitive vs boxed
val a: Int = 42           // primitive int
val b: Int? = 42          // boxed Integer
val c: List<Int> = listOf(1, 2, 3)        // primitive-backed list (tergantung implementasi)
val d: List<Int?> = listOf(1, null, 3)     // boxed Integer list

Dampak performa dan memory #

KonteksRepresentasi JVMMemori per elemenCatatan
Int non-nullable lokalint primitive4 bytetercepat, paling hemat
Int? nullableInteger boxed16+ byteada overhead header objek
Array<Int>Integer[]16+ byte per elemenselalu boxed
IntArrayint[]4 byte per elemenprimitive array khusus

Referensi Lanjutan #

  • Basic types — pengantar tipe dasar Kotlin yang menjelaskan representasi primitive vs reference di JVM
  • Numbers — dokumentasi tipe angka Kotlin termasuk boxing otomatis untuk tipe reference

Pemahaman tentang bagaimana Kotlin memetakan tipe ke JVM membantu kita menulis kode yang efisien. Berikutnya, kita akan menjelajahi seluruh keluarga tipe angka — Byte, Short, Int, Long, Float, Double — pada chapter Numbers.