Beberapa domain data hanya mengandung nilai non-negatif: ukuran file, ID database, port jaringan, dan bit mask. Untuk kasus ini, Kotlin menyediakan empat tipe integer unsigned: UByte, UShort, UInt, dan ULong. Tipe-tipe ini memperluas rentang positif dari padanan signed-nya tanpa menambah ukuran memori. Pada chapter ini kita akan membedah deklarasi, rentang, suffix literal, serta kapan memakai tipe unsigned dan kapan menghindarinya.

Empat tipe unsigned #

TipeLebarRentangPadanan signed
UByte8 bit0 .. 255Byte (-128 .. 127)
UShort16 bit0 .. 65.535Short
UInt32 bit0 .. 4.294.967.295Int
ULong64 bit0 .. 18.446.744.073.709.551.615Long

Memori yang digunakan sama dengan padanan signed — UByte tetap 1 byte, ULong tetap 8 byte. Perbedaannya hanya pada interpretasi bit (ada bit tanda atau tidak).

Literal dan suffix #

Literal unsigned menggunakan suffix u atau U:

kotlin Literal unsigned
val a: UByte = 200u
val b: UShort = 60_000u
val c: UInt = 4_000_000_000u
val d: ULong = 18_000_000_000_000_000_000u

Aturan inferensi tipe unsigned:

  • 42u tanpa tipe eksplisit disimpulkan sebagai UInt jika muat, atau ULong jika melampaui UInt.MAX_VALUE.
  • Untuk memaksakan ULong walaupun nilai kecil, gunakan 42uL atau 42UL.
  • Tipe eksplisit (val x: UByte = 200u) memaksa literal ditafsirkan sebagai tipe tersebut.

Use case tipe unsigned #

Beberapa domain yang cocok memakai tipe unsigned:

Use caseTipe yang disarankanAlasan
Ukuran file dalam byteULongBisa melampaui Long.MAX_VALUE untuk file besar
Port jaringan (0-65535)UShortPort tidak pernah negatif
Komponen warna RGB (0-255)UByteRange tepat 0..255
ID database di atas 2^31UInt atau ULongMenghindari overflow yang tidak perlu
Bit mask / flagUInt atau ULongOperasi bit lebih intuitif tanpa tanda

Demonstrasi unsigned #

Berikut program lengkap yang memperlihatkan deklarasi, rentang, use case, dan operasi bit:

kotlin
fun main() {
    val uByte: UByte = 200u
    val uShort: UShort = 60_000u
    val uInt: UInt = 4_000_000_000u
    val uLong: ULong = 18_000_000_000_000_000_000u

    println("uByte  = $uByte  (size ${UByte.SIZE_BITS} bit)")
    println("uShort = $uShort (size ${UShort.SIZE_BITS} bit)")
    println("uInt   = $uInt")
    println("uLong  = $uLong")

    println()
    println("Range tipe unsigned:")
    println("UByte  : ${UByte.MIN_VALUE} .. ${UByte.MAX_VALUE}")
    println("UShort : ${UShort.MIN_VALUE} .. ${UShort.MAX_VALUE}")
    println("UInt   : ${UInt.MIN_VALUE} .. ${UInt.MAX_VALUE}")
    println("ULong  : ${ULong.MIN_VALUE} .. ${ULong.MAX_VALUE}")

    println()
    val ukuranFileByte: ULong = 5_000_000_000u
    println("Ukuran file (byte) = $ukuranFileByte")
    println("Konversi ke MB     = ${ukuranFileByte / 1_048_576u} MB (1_048_576 byte = 1 MB)")

    println()
    val maskUByte: UByte = 0xFFu
    val flagA: UByte = 0b0000_1010u
    val flagB: UByte = 0b0001_0100u
    val kombinasi = flagA or flagB
    println("maskUByte = $maskUByte")
    println("flagA (0b0000_1010) = ${flagA.toString(2).padStart(8, '0')}")
    println("flagB (0b0001_0100) = ${flagB.toString(2).padStart(8, '0')}")
    println("flagA or flagB      = ${kombinasi.toString(2).padStart(8, '0')}")

    println()
    val a: UInt = 42u
    val b = a.toInt()
    println("a (UInt) = $a -> toInt() = $b (${b.javaClass.simpleName})")

    println()
    val maxInt = Int.MAX_VALUE.toUInt()
    val tambah = maxInt + 1u
    println("Int.MAX_VALUE.toUInt() = $maxInt")
    println("tambah 1u              = $tambah (tidak overflow karena unsigned)")
}

Output yang Dihasilkan:

text
uByte  = 200  (size 8 bit)
uShort = 60000 (size 16 bit)
uInt   = 4000000000
uLong  = 18000000000000000000

Range tipe unsigned:
UByte  : 0 .. 255
UShort : 0 .. 65535
UInt   : 0 .. 4294967295
ULong  : 0 .. 18446744073709551615

Ukuran file (byte) = 5000000000
Konversi ke MB     = 4768 MB (1_048_576 byte = 1 MB)

maskUByte = 255
flagA (0b0000_1010) = 00001010
flagB (0b0001_0100) = 00010100
flagA or flagB      = 00011110

a (UInt) = 42 -> toInt() = 42 (int)

Int.MAX_VALUE.toUInt() = 2147483647
tambah 1u              = 2147483648 (tidak overflow karena unsigned)

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

  1. Baris 2-5 — deklarasi dengan tipe eksplisit. Suffix u wajib agar literal ditafsirkan sebagai unsigned; tanpa suffix, 200 akan menjadi Int (dan gagal karena di luar range Byte signed).
  2. Baris 13-16 (MIN_VALUE, MAX_VALUE) — MIN_VALUE untuk semua tipe unsigned adalah 0. MAX_VALUE adalah 2^N - 1.
  3. Baris 19 (ukuranFileByte / 1_048_576u) — pembagian unsigned wajib literal unsigned (1_048_576u). Hasilnya 4768 MB, yang akurat untuk 5 GB.
  4. Baris 25 (flagA or flagB) — operator or (bisa juga infix) melakukan operasi bitwise OR. Hasil 0b0000_1010 or 0b0001_0100 = 0b0001_1110. Method .toString(2).padStart(8, '0') memformat sebagai biner 8-bit.
  5. Baris 31-32 (a.toInt()) — konversi eksplisit UInt → Int. Bila nilai melampaui Int.MAX_VALUE, hasil akan ter-wrap-around ke nilai negatif — tidak ada exception.
  6. Baris 35-36 (Int.MAX_VALUE.toUInt() + 1u) — konversi Int → UInt lalu penambahan. Hasil 2.147.483.648 — tidak overflow karena UInt.MAX_VALUE adalah 4.294.967.295. Ini menunjukkan keuntungan utama unsigned.

Operasi bitwise #

Tipe unsigned mendukung operasi bitwise yang sama dengan signed:

OperatorNamaContoh
orOR bitwise0b1010u or 0b0101u
andAND bitwise0b1110u and 0b1011u
xorXOR bitwise0b1010u xor 0b0011u
inv()NOT bitwise (inversi)0b00001111u.inv()
shl(bits)Shift left1u shl 4
shr(bits)Shift right logical0b10000000u shr 4

Konversi signed ↔ unsigned #

Konversi antara signed dan unsigned bersifat eksplisit — kompilator tidak melakukan konversi implisit. Method yang tersedia: .toUByte(), .toUShort(), .toUInt(), .toULong(), dan sebaliknya .toByte(), .toShort(), .toInt(), .toLong().

kotlin Konversi signed-unsigned
val signed: Int = -1
val unsigned: UInt = signed.toUInt()   // 4294967295 (semua bit 1)
val kembali: Int = unsigned.toInt()    // -1 (kembali ke nilai asli)

Referensi Lanjutan #

  • Unsigned integers — dokumentasi resmi tipe unsigned Kotlin UByte, UShort, UInt, dan ULong
  • Numbers — rujukan tipe angka Kotlin sebagai pembanding untuk representasi signed vs unsigned

Pemilihan tipe unsigned bergantung pada konteks data. Berikutnya, kita akan melihat tipe-tipe khusus Kotlin yang berperan sebagai akar hierarki tipe — Any, Unit, dan Nothing — pada chapter Any, Unit, dan Nothing.