Berkutat dengan beberapa tipe data berbeda adalah hal biasa dalam program. Di beberapa bahasa, konversi antar tipe numerik terjadi otomatis (implicit), yang sering menyembunyikan kehilangan presisi atau overflow. Kotlin memilih jalur berbeda: tidak ada konversi numerik implisit — semua konversi wajib eksplisit melalui method seperti .toInt(), .toLong(), atau .toDouble(). Pada chapter ini kita akan membedah konversi tipe, kapan terjadi truncation, kapan terjadi wrap-around, serta konversi dari/ke String dan Char.
Tidak ada konversi implisit #
Berbeda dari Java atau C yang memperbolehkan long l = someInt;, Kotlin menolak kode seperti itu:
val i: Int = 5
// val l: Long = i // galat: type mismatch
val l: Long = i.toLong() // sah: konversi eksplisitAturan ini sengaja diadopsi untuk mencegah kesalahan halus seperti kehilangan presisi saat konversi Long → Int yang tidak disadari. Dengan memaksa eksplisit, pembaca kode menyadari adanya konversi.
Method konversi numerik #
Setiap tipe numerik memiliki method konversi ke semua tipe numerik lain:
| Dari | Method tersedia |
|---|---|
Byte | .toShort(), .toInt(), .toLong(), .toFloat(), .toDouble(), .toChar() |
Short | .toByte(), .toInt(), .toLong(), .toFloat(), .toDouble(), .toChar() |
Int | .toByte(), .toShort(), .toLong(), .toFloat(), .toDouble(), .toChar() |
Long | .toByte(), .toShort(), .toInt(), .toFloat(), .toDouble(), .toChar() |
Float | .toByte(), .toShort(), .toInt(), .toLong(), .toDouble(), .toChar() |
Double | .toByte(), .toShort(), .toInt(), .toLong(), .toFloat(), .toChar() |
Demonstrasi konversi #
Berikut program lengkap yang memperlihatkan berbagai konversi:
fun main() {
val umurInt: Int = 25
val umurLong: Long = umurInt.toLong()
val umurDouble: Double = umurInt.toDouble()
val umurFloat: Float = umurInt.toFloat()
val umurByte: Byte = umurInt.toByte()
val umurString: String = umurInt.toString()
println("umurInt = $umurInt (${umurInt.javaClass.simpleName})")
println("umurLong = $umurLong (${umurLong.javaClass.simpleName})")
println("umurDouble = $umurDouble (${umurDouble.javaClass.simpleName})")
println("umurFloat = $umurFloat (${umurFloat.javaClass.simpleName})")
println("umurByte = $umurByte (${umurByte.javaClass.simpleName})")
println("umurString = $umurString (${umurString.javaClass.simpleName})")
println()
val beratString = "65.5"
val beratDouble = beratString.toDouble()
val beratFloat = beratString.toFloat()
val beratInt = beratDouble.toInt()
println("beratString = $beratString")
println("beratDouble = $beratDouble")
println("beratFloat = $beratFloat")
println("beratInt = $beratInt (truncated)")
println()
val maxInt = Int.MAX_VALUE
val maxLongFromInt = maxInt.toLong()
val overflowInt = (maxLongFromInt + 1).toInt()
println("Int.MAX_VALUE = $maxInt")
println("maxInt.toLong() = $maxLongFromInt")
println("(maxInt+1).toInt() = $overflowInt (wrap-around)")
println()
val nilaiDouble = 3.99
val dibulatkan = nilaiDouble.toInt()
val dibulatkanRound = Math.round(nilaiDouble).toInt()
println("nilaiDouble = $nilaiDouble")
println("toInt() = $dibulatkan (truncation ke nol)")
println("Math.round() = $dibulatkanRound")
println()
val huruf = '7'
val kode = huruf.code
val angkaDariHuruf = huruf.digitToInt()
println("huruf = $huruf")
println("huruf.code = $kode")
println("huruf.digitToInt() = $angkaDariHuruf")
}Output yang Dihasilkan:
umurInt = 25 (int)
umurLong = 25 (long)
umurDouble = 25.0 (double)
umurFloat = 25.0 (float)
umurByte = 25 (byte)
umurString = 25 (String)
beratString = 65.5
beratDouble = 65.5
beratFloat = 65.5
beratInt = 65 (truncated)
Int.MAX_VALUE = 2147483647
maxInt.toLong() = 2147483647
(maxInt+1).toInt() = -2147483648 (wrap-around)
nilaiDouble = 3.99
toInt() = 3 (truncation ke nol)
Math.round() = 4
huruf = 7
huruf.code = 55
huruf.digitToInt() = 7Mari kita amati baris-baris penting pada kode di atas:
- Baris 2-8 — konversi
Intke semua tipe numerik lain.toLong()memperluas nilai tanpa kehilangan;toFloat()/toDouble()mempertahankan nilai;toByte()memotong bila nilai di luar rangeByte. - Baris 9 (
umurString = umurInt.toString()) — konversi numerik keStringviatoString(). Berguna untuk penyusunan pesan atau penyimpanan ke file teks. - Baris 13-17 (
beratString.toDouble(), dst.) — konversiStringke numerik."65.5".toDouble()menghasilkan65.5. Bila string tidak valid, akan memunculkanNumberFormatException. - Baris 18 (
beratInt = beratDouble.toInt()) — konversiDouble → Intbersifat truncation (pemotongan ke nol).65.5menjadi65;-65.5menjadi-65. - Baris 23-25 (
(maxLongFromInt + 1).toInt()) —maxInt.toLong() + 1menghasilkan2.147.483.648(di luarInt.MAX_VALUE)..toInt()memotong bit ke-32 dan menyebabkan wrap-around ke-2.147.483.648. Tidak ada exception yang dilempar. - Baris 30-31 (
toInt()vsMath.round()) —toInt()memotong desimal;Math.round()membulatkan ke bilangan bulat terdekat (0.5 ke atas). - Baris 35-37 (
huruf.code,huruf.digitToInt()) —.codemengembalikan kode Unicode ('7'.code = 55);.digitToInt()mengembalikan nilai numerik digit ('7'.digitToInt() = 7). Method kedua hanya valid untuk karakter'0'..'9'.
Konversi String ke numerik #
Beberapa method untuk parse String menjadi numerik:
| Method | Tipe hasil | Contoh | Exception bila invalid |
|---|---|---|---|
.toInt() | Int | "42".toInt() | NumberFormatException |
.toLong() | Long | "42L".toLong() gagal | NumberFormatException |
.toDouble() | Double | "3.14".toDouble() | NumberFormatException |
.toFloat() | Float | "3.14f".toFloat() gagal | NumberFormatException |
.toIntOrNull() | Int? | "abc".toIntOrNull() → null | (tidak ada exception) |
.toDoubleOrNull() | Double? | "x".toDoubleOrNull() → null | (tidak ada exception) |
Konversi Char ↔ Int #
Char dan Int dipisahkan secara tipe, tetapi konversi dua arah tersedia:
val huruf: Char = 'A'
val kode: Int = huruf.code // 65
val kembali: Char = kode.toChar() // 'A'
val digit: Char = '5'
val nilai: Int = digit.digitToInt() // 5.digitToInt() hanya bekerja untuk '0'..'9'. Untuk karakter di luar rentang ini, gunakan .code untuk mendapatkan nilai Unicode.
Truncation vs rounding #
| Operasi | Hasil untuk 3.99 | Hasil untuk -3.99 |
|---|---|---|
.toInt() | 3 | -3 (truncation menuju nol) |
Math.round().toInt() | 4 | -3 (pembulatan ke terdekat) |
Math.floor().toInt() | 3 | -4 (pembulatan ke bawah) |
Math.ceil().toInt() | 4 | -3 (pembulatan ke atas) |
Referensi Lanjutan #
- Type checks and casts
— dokumentasi resmi type checks dan casts Kotlin termasuk
as,as?, danis - Basic types — rujukan tipe dasar Kotlin untuk memahami konversi eksplisit antar tipe angka
Konversi eksplisit membuat kode lebih aman karena setiap langkah konversi terlihat. Berikutnya, kita akan melihat bagaimana menyederhanakan tipe kompleks dengan type alias — pada chapter Type Aliases.