Tipe Data

Tipe Data #

Setiap nilai dalam program punya tipe — dan tipe menentukan apa yang bisa kamu lakukan dengan nilai itu. Di Java, ada perbedaan mendasar antara tipe primitif (int, double, boolean) dan tipe objek (Integer, Double, Boolean). Kotlin menghapus perbedaan ini: semua tipe adalah objek. Tapi jangan khawatir soal performa — compiler Kotlin cukup cerdas untuk menggunakan representasi primitif JVM di balik layar saat memungkinkan. Hasilnya: kode lebih bersih tanpa boxing/unboxing manual, dengan performa yang setara. Artikel ini membahas seluruh tipe data bawaan Kotlin secara mendalam, termasuk cara konversi, operasi yang tersedia, dan tipe-tipe khusus yang tidak ada di bahasa lain.

Tipe Numerik #

Kotlin menyediakan enam tipe numerik: empat untuk bilangan bulat dan dua untuk bilangan desimal.

Bilangan Bulat (Integer) #

TipeUkuranRentang Nilai
Byte8 bit-128 sampai 127
Short16 bit-32.768 sampai 32.767
Int32 bit-2.147.483.648 sampai 2.147.483.647 (~2,1 miliar)
Long64 bit-9,2 × 10¹⁸ sampai 9,2 × 10¹⁸

Untuk kebanyakan keperluan sehari-hari, Int sudah mencukupi. Gunakan Long untuk nilai yang melebihi kapasitas Int — seperti timestamp Unix dalam milidetik, ID database yang sangat besar, atau hitungan byte file besar.

val umur: Byte = 25
val tahun: Short = 2024
val populasiKota: Int = 10_500_000
val timestampMs: Long = System.currentTimeMillis()

// Suffix L untuk Long literal
val jarakBintangMeter = 9_461_000_000_000_000L  // 1 tahun cahaya dalam meter

Kotlin mendukung underscore sebagai pemisah digit untuk keterbacaan — compiler mengabaikannya sepenuhnya:

val sejuta = 1_000_000
val hexWarna = 0xFF_33_AA
val biner = 0b1010_1100_1111_0000
val oktalbytes = 255

Bilangan Desimal (Floating-Point) #

TipeUkuranPresisiRentang Perkiraan
Float32 bit~6–7 digit desimal±3.4 × 10³⁸
Double64 bit~15–16 digit desimal±1.7 × 10³⁰⁸

Double adalah default — literal 3.14 tanpa suffix diinfer sebagai Double. Gunakan suffix f atau F untuk Float:

val suhu: Double = 36.6          // Double (default)
val suhuFloat: Float = 36.6f     // Float — butuh suffix f

val phi = 3.141592653589793      // Double — presisi penuh
val phiFloat = 3.1415927f        // Float — presisi terpotong

Jangan gunakan Float atau Double untuk nilai keuangan (uang). Representasi floating-point tidak bisa merepresentasikan semua angka desimal secara tepat, sehingga perhitungan uang bisa menghasilkan error kecil yang terakumulasi. Gunakan BigDecimal untuk uang.

// ANTI-PATTERN: perhitungan uang dengan Double
val harga = 0.1 + 0.2
println(harga)  // 0.30000000000000004 — bukan 0.3!

// BENAR: gunakan BigDecimal
import java.math.BigDecimal
val hargaTepat = BigDecimal("0.1") + BigDecimal("0.2")
println(hargaTepat)  // 0.3

Konversi Tipe Numerik #

Kotlin tidak melakukan konversi numerik implisit — berbeda dari Java. Kamu harus selalu konversi secara eksplisit menggunakan fungsi konversi yang tersedia.

val angkaInt: Int = 42

// ANTI-PATTERN: assignment langsung antar tipe numerik
val angkaLong: Long = angkaInt  // ✗ error kompilasi di Kotlin!

// BENAR: konversi eksplisit
val angkaLong: Long = angkaInt.toLong()
val angkaDouble: Double = angkaInt.toDouble()
val angkaFloat: Float = angkaInt.toFloat()
val angkaByte: Byte = angkaInt.toByte()
val angkaShort: Short = angkaInt.toShort()

Fungsi konversi yang tersedia untuk semua tipe numerik:

FungsiHasil
.toByte()Byte
.toShort()Short
.toInt()Int
.toLong()Long
.toFloat()Float
.toDouble()Double
.toChar()Char
.toString()String

Konversi String ke Angka #

val teks = "42"
val angka = teks.toInt()          // 42
val desimal = "3.14".toDouble()   // 3.14

// Konversi aman — kembalikan null jika gagal, bukan exception
val valid = "123".toIntOrNull()     // 123
val invalid = "abc".toIntOrNull()   // null
val gagal = "99.9".toIntOrNull()    // null (bukan integer)

// Gunakan Elvis untuk nilai default
val nilai = "bukan angka".toIntOrNull() ?: 0  // 0

Overflow — Perhatikan Batasnya #

val maks = Int.MAX_VALUE         // 2_147_483_647
val overflow = maks + 1          // -2_147_483_648 — bukan error, tapi salah!

// BENAR: gunakan Long jika nilai bisa melebihi Int.MAX_VALUE
val maksAman = Int.MAX_VALUE.toLong() + 1  // 2_147_483_648L

Char — Tipe Karakter #

Char menyimpan satu karakter Unicode. Ditulis dengan tanda kutip tunggal.

val huruf: Char = 'A'
val angkaKarakter: Char = '7'
val simbol: Char = '@'
val unicode: Char = '\u0041'  // 'A' dalam notasi Unicode escape

println(huruf.code)           // 65 — nilai kode ASCII/Unicode
println(huruf.isLetter())     // true
println(huruf.isUpperCase())  // true
println(huruf.lowercaseChar()) // 'a'

Char di Kotlin adalah tipe tersendiri — bukan angka. Di Java, char bisa digunakan langsung sebagai int. Di Kotlin, konversi harus eksplisit:

val karakter = 'A'

// ANTI-PATTERN: perlakukan Char sebagai angka langsung
val kode: Int = karakter  // ✗ error kompilasi

// BENAR: konversi eksplisit
val kode: Int = karakter.code        // 65
val kodeChar: Char = 65.toChar()     // 'A'

Escape Character #

val newline = '\n'     // baris baru
val tab = '\t'         // tab
val backslash = '\\'   // backslash
val kutipTunggal = '\'' // tanda kutip tunggal
val unicode = '\u2764' // ❤ — karakter Unicode

Boolean — Tipe Logika #

Boolean hanya punya dua nilai: true dan false. Digunakan untuk kondisi, flag, dan kontrol alur.

val aktif: Boolean = true
val sudahVerifikasi = false   // type inference

// Operator logika
val danLogika = true && false   // false — AND
val atauLogika = true || false  // true — OR
val negasi = !true              // false — NOT

// Short-circuit evaluation
// && berhenti evaluasi jika kiri sudah false
// || berhenti evaluasi jika kiri sudah true
val aman = daftar.isNotEmpty() && daftar[0] != null

Fungsi Ekstensi Boolean yang Berguna #

val kondisi = 10 > 5

// takeIf dan takeUnless
val nilai = kondisi.takeIf { it }     // true jika kondisi true, null jika false

// Konversi ke String
println(kondisi.toString())  // "true"

String — Tipe Teks #

String menyimpan urutan karakter yang bersifat immutable. Setiap operasi pada string menghasilkan string baru, bukan mengubah yang sudah ada.

val salam = "Halo, Dunia!"
val kosong = ""
val spasi = "   "

println(salam.length)         // 12
println(salam.uppercase())    // HALO, DUNIA!
println(salam.lowercase())    // halo, dunia!
println(salam.reversed())     // !ainuD ,olaH
println(salam.isEmpty())      // false
println(kosong.isEmpty())     // true
println(spasi.isBlank())      // true — isBlank() cek isEmpty() atau hanya spasi

String Template #

val nama = "Budi"
val umur = 28

// Interpolasi variabel
val perkenalan = "Nama saya $nama, umur $umur tahun."

// Interpolasi ekspresi
val info = "Panjang nama: ${nama.length} karakter"
val status = "Status: ${if (umur >= 18) "Dewasa" else "Anak-anak"}"
val perhitungan = "Dua kali umur: ${umur * 2}"

Raw String (Multiline) #

Raw string menggunakan triple quote dan mempertahankan semua karakter termasuk newline dan tab — tanpa perlu escape.

val json = """
    {
        "nama": "Budi Santoso",
        "umur": 28,
        "kota": "Jakarta"
    }
""".trimIndent()

val query = """
    SELECT u.nama, u.email
    FROM pengguna u
    WHERE u.aktif = true
      AND u.umur >= 18
    ORDER BY u.nama
""".trimIndent()

println(json)

.trimIndent() menghapus indentasi leading yang sama di semua baris — hasilnya string tanpa indentasi berlebih.

Operasi String yang Sering Digunakan #

val teks = "  Kotlin Programming  "

// Pembersihan
println(teks.trim())           // "Kotlin Programming"
println(teks.trimStart())      // "Kotlin Programming  "
println(teks.trimEnd())        // "  Kotlin Programming"

// Pemeriksaan
println(teks.contains("Kotlin"))        // true
println(teks.startsWith("  Kot"))       // true
println(teks.endsWith("ing  "))         // true

// Manipulasi
println(teks.replace("Kotlin", "Swift")) // "  Swift Programming  "
println(teks.trim().split(" "))          // [Kotlin, Programming]

val kata = "kotlin"
println(kata.capitalize())             // Kotlin (deprecated di versi terbaru)
println(kata.replaceFirstChar { it.uppercase() })  // Kotlin (cara modern)

// Substring
val bahasa = "Kotlin Programming"
println(bahasa.substring(0, 6))        // Kotlin
println(bahasa.substringAfter(" "))    // Programming
println(bahasa.substringBefore(" "))   // Kotlin

String Immutability dan StringBuilder #

Karena String immutable, operasi berulang yang membangun string (seperti dalam loop) sebaiknya menggunakan StringBuilder:

// ANTI-PATTERN: konkatenasi string dalam loop — buat objek baru setiap iterasi
var hasil = ""
for (i in 1..1000) {
    hasil += "item-$i,"  // sangat tidak efisien untuk loop besar
}

// BENAR: gunakan StringBuilder
val builder = StringBuilder()
for (i in 1..1000) {
    builder.append("item-$i,")
}
val hasil = builder.toString()

// Atau gunakan joinToString untuk koleksi
val hasil = (1..1000).joinToString(",") { "item-$it" }

Array #

Array adalah struktur data berukuran tetap yang menyimpan elemen-elemen dari tipe yang sama. Ukurannya tidak bisa diubah setelah dibuat.

// Array generik
val buah: Array<String> = arrayOf("Mangga", "Apel", "Jeruk")
val angka: Array<Int> = arrayOf(1, 2, 3, 4, 5)

// Array dengan ukuran dan nilai default
val nol = Array(5) { 0 }           // [0, 0, 0, 0, 0]
val kuadrat = Array(5) { i -> i * i }  // [0, 1, 4, 9, 16]

// Akses elemen
println(buah[0])      // Mangga
println(buah.size)    // 3

buah[1] = "Pisang"    // modifikasi nilai
println(buah[1])      // Pisang

Array Primitif — Lebih Efisien #

Untuk tipe numerik, Kotlin menyediakan array primitif yang tidak melakukan boxing — performanya setara dengan array Java primitif:

val byteArray = byteArrayOf(1, 2, 3)
val shortArray = shortArrayOf(10, 20, 30)
val intArray = intArrayOf(1, 2, 3, 4, 5)
val longArray = longArrayOf(1L, 2L, 3L)
val floatArray = floatArrayOf(1.0f, 2.5f, 3.7f)
val doubleArray = doubleArrayOf(1.0, 2.5, 3.14)
val booleanArray = booleanArrayOf(true, false, true)
val charArray = charArrayOf('K', 'o', 't', 'l', 'i', 'n')

Array vs List — Kapan Memilih #

GUNAKAN Array jika:
  ✓ Bekerja dengan API Java yang membutuhkan array
  ✓ Performa kritis dan kamu butuh array primitif
  ✓ Ukuran sudah pasti dan tidak berubah
  ✓ Interoperabilitas dengan kode Java

GUNAKAN List jika:
  ✓ Ukuran mungkin berubah (MutableList)
  ✓ Butuh operasi koleksi: filter, map, sortedBy, dll
  ✓ Kode yang lebih ekspresif dan idiomatic Kotlin
  ✓ Mayoritas use case sehari-hari

Tipe Khusus: Any, Unit, Nothing #

Ini adalah tiga tipe yang ada di puncak dan dasar hierarki tipe Kotlin — dan masing-masing punya peran unik.

flowchart TD
    A["Any\n(supertype semua tipe non-null)"] --> B[String]
    A --> C[Int]
    A --> D[Boolean]
    A --> E[Kelas kustom...]
    F["Any?\n(supertype semua tipe termasuk null)"] --> A
    F --> G[null]
    H["Nothing\n(subtype semua tipe — tidak pernah ada nilainya)"] --> B
    H --> C
    H --> D

Any — Supertype Semua Tipe #

Any adalah induk dari semua tipe non-nullable di Kotlin. Setara dengan Object di Java, tapi lebih bersih karena tidak harus bergulat dengan primitif.

val apa: Any = "Bisa berisi apa saja"
val juga: Any = 42
val atau: Any = true
val bahkan: Any = listOf(1, 2, 3)

// Any punya tiga method dasar
println(apa.toString())       // representasi string
println(apa.hashCode())       // hash code
println(apa.equals("test"))   // perbandingan kesetaraan

// Type check dan smart cast
fun deskripsikan(nilai: Any): String {
    return when (nilai) {
        is String  -> "String dengan panjang ${nilai.length}"
        is Int     -> "Integer: ${nilai * 2}"
        is Boolean -> "Boolean: ${if (nilai) "benar" else "salah"}"
        is List<*> -> "List dengan ${nilai.size} elemen"
        else       -> "Tipe tidak dikenal: ${nilai::class.simpleName}"
    }
}

println(deskripsikan("Kotlin"))   // String dengan panjang 6
println(deskripsikan(21))         // Integer: 42
println(deskripsikan(true))       // Boolean: benar

Unit — Pengganti void #

Unit adalah tipe return untuk fungsi yang tidak mengembalikan nilai bermakna. Ini setara dengan void di Java, tapi Unit adalah tipe nyata — bisa disimpan dalam variabel dan digunakan sebagai type argument generik.

// Dua cara ini identik — Unit bisa ditulis eksplisit atau dihilangkan
fun cetakPesan(pesan: String): Unit {
    println(pesan)
}

fun cetakPesan2(pesan: String) {  // Unit tersirat
    println(pesan)
}

// Unit berguna dalam generik
val aksi: () -> Unit = { println("Dieksekusi!") }
val daftar: List<() -> Unit> = listOf(
    { println("Aksi 1") },
    { println("Aksi 2") }
)

daftar.forEach { it() }

Nothing — Tipe yang Tidak Pernah Ada #

Nothing adalah tipe yang tidak pernah memiliki instance. Ia adalah subtype dari semua tipe lain, yang artinya bisa digunakan di mana pun tipe apapun diharapkan. Digunakan untuk fungsi yang tidak pernah kembali secara normal — selalu melempar exception atau berjalan selamanya.

// Fungsi yang selalu melempar exception
fun gagal(pesan: String): Nothing {
    throw IllegalStateException(pesan)
}

// Berguna untuk validasi yang mengakhiri eksekusi
fun ambilNilai(map: Map<String, Int>, kunci: String): Int {
    return map[kunci] ?: gagal("Kunci '$kunci' tidak ditemukan")
    // Compiler tahu: jika map[kunci] null, gagal() tidak pernah return
    // Sehingga ekspresi ini selalu menghasilkan Int — aman!
}

// Fungsi yang berjalan selamanya
fun loopSelamanya(): Nothing {
    while (true) {
        Thread.sleep(1000)
        println("Masih berjalan...")
    }
}

Nothing juga digunakan sebagai tipe dari ekspresi throw:

val nama: String = ambilNamaDariDb()
    ?: throw IllegalStateException("Nama harus ada")
// Tipe ekspresi throw adalah Nothing
// Nothing adalah subtype String
// Sehingga ekspresi keseluruhan bertipe String ✓

Sistem Tipe Kotlin: Non-Nullable dan Nullable #

Semua tipe di Kotlin hadir dalam dua versi: non-nullable (default) dan nullable (dengan ?).

flowchart LR
    A["String\n(tidak bisa null)"] -- "tambahkan ?" --> B["String?\n(bisa null atau String)"]
    C["Int\n(tidak bisa null)"] -- "tambahkan ?" --> D["Int?\n(bisa null atau Int)"]
    E["Any\n(semua non-null)"] -- "tambahkan ?" --> F["Any?\n(semua tipe termasuk null)"]
// Non-nullable — compiler menjamin tidak pernah null
var nama: String = "Budi"
var umur: Int = 25

// Nullable — harus ditangani sebelum digunakan
var alamat: String? = null
var nilaiOpsional: Int? = null

// Type checking dengan is
fun prosesNilai(nilai: Any?) {
    when {
        nilai == null        -> println("Null")
        nilai is String      -> println("String: $nilai")
        nilai is Int         -> println("Int: $nilai")
        nilai is Double      -> println("Double: $nilai")
        else                 -> println("Tipe lain: ${nilai::class.simpleName}")
    }
}

Type Casting Aman #

val nilai: Any = "Kotlin"

// Safe cast — kembalikan null jika gagal (bukan exception)
val teks: String? = nilai as? String    // "Kotlin"
val angka: Int? = nilai as? Int         // null — bukan Int

// Unsafe cast — exception jika gagal
val teks2: String = nilai as String     // OK
// val angka2: Int = nilai as Int        // ✗ ClassCastException!

Ringkasan #

  • Enam tipe numerikByte, Short, Int, Long untuk bilangan bulat; Float, Double untuk desimal. Int dan Double adalah default untuk masing-masing kategori.
  • Tidak ada konversi implisit — Kotlin memaksa konversi eksplisit antar tipe numerik dengan .toInt(), .toLong(), .toDouble(), dll. Ini mencegah bug yang tidak disengaja.
  • Gunakan BigDecimal untuk uangFloat dan Double tidak bisa merepresentasikan semua desimal secara tepat; perhitungan keuangan harus menggunakan BigDecimal.
  • Char bukan angka — berbeda dari Java, Char di Kotlin adalah tipe tersendiri. Konversi ke angka menggunakan .code, konversi sebaliknya dengan .toChar().
  • String immutable — setiap operasi string menghasilkan objek baru. Untuk operasi berulang dalam loop, gunakan StringBuilder atau joinToString().
  • Array vs ListArray untuk interop Java dan performa kritis; List untuk kode Kotlin sehari-hari karena lebih ekspresif dengan dukungan operasi koleksi lengkap.
  • Any adalah supertype — semua tipe non-nullable mewarisi dari Any. Any? mencakup juga nilai null.
  • Unit adalah void yang nyata — tipe return fungsi tanpa nilai bermakna. Berbeda dari void, Unit bisa digunakan sebagai type argument generik.
  • Nothing tidak pernah ada — tipe fungsi yang tidak pernah return normal (selalu throw atau loop selamanya). Berguna sebagai sinyal kepada compiler bahwa cabang kode tertentu tidak bisa dicapai.
  • Setiap tipe hadir dalam dua versi — non-nullable (default, aman) dan nullable (dengan ?, harus ditangani). Pilih non-nullable sebagai default; tambahkan ? hanya jika null memang valid secara semantik.

← Sebelumnya: Konstanta   Berikutnya: Operator →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact