Scala adalah bahasa yang bertipe statis, yaitu setiap nilai memiliki tipe yang diperiksa compiler sebelum program berjalan. Di dasar sistem tipe Scala terdapat tipe-tipe dasar untuk angka bulat, angka pecahan, nilai kebenaran, dan ketiadaan hasil. Topik ini membahas Int, Long, Double, Boolean, Unit, dan Nothing, kemudian menjelaskan cara mengonversi nilai di antara tipe-tipe angka beserta sifat pemotongan yang menyertainya.
Tipe bilangan bulat dan pecahan#
Tipe angka yang paling sering digunakan adalah Int untuk bilangan bulat 32 bit, Long untuk bilangan bulat 64 bit, dan Double untuk bilangan pecahan presisi ganda 64 bit. Sementara itu, Boolean hanya memiliki dua nilai, yaitu true dan false.
Simpan program berikut sebagai tipe-dasar.scala.
// Tipe-tipe dasar yang paling sering digunakan
@main def tipeDasar(): Unit =
val jumlahBaris: Int = 42
val populasi: Long = 7_800_000L
val suhu: Double = 36.6
val sah: Boolean = true
println("Int : " + jumlahBaris)
println("Long : " + populasi)
println("Double : " + suhu)
println("Boolean: " + sah)
println("Batas atas Int : " + Int.MaxValue)
println("Batas atas Long: " + Long.MaxValue)
println("0.1 + 0.2 : " + (0.1 + 0.2))Jalankan program tersebut dengan scala-cli.
# mengompilasi dan menjalankan program tipe dasar
scala-cli run tipe-dasar.scalaInt : 42
Long : 7800000
Double : 36.6
Boolean: true
Batas atas Int : 2147483647
Batas atas Long: 9223372036854775807
0.1 + 0.2 : 0.30000000000000004Beberapa detail penulisan pada program di atas patut dicatat. Akhiran L menandai literal Long, sedangkan garis bawah sebagai pemisah ribuan pada 7_800_000L hanya mempermudah pembacaan dan tidak mengubah nilainya. Anotasi tipe ditulis secara eksplisit pada setiap pengikatan agar tipe yang diinginkan tampak jelas, walaupun compiler juga mampu menentukannya sendiri.
Jangkauan masing-masing tipe dirangkum pada tabel berikut.
| Tipe | Jangkauan | Ukuran |
|---|---|---|
| Int | -2.147.483.648 sampai 2.147.483.647 | 32 bit |
| Long | -9.223.372.036.854.775.808 sampai 9.223.372.036.854.775.807 | 64 bit |
| Double | sekitar , dengan sekitar 15 angka penting | 64 bit |
| Boolean | true atau false | bergantung pada JVM, sekurang-kurangnya 1 byte |
Baris terakhir keluaran menunjukkan sifat Double yang perlu diwaspadai. Nilai pecahan disimpan sebagai perkiraan biner, sehingga penjumlahan 0.1 + 0.2 menghasilkan 0.30000000000000004 dan bukan 0.3 yang tepat. Penyimpangan sekecil ini lazim pada semua bahasa yang menggunakan aritmetika pecahan IEEE 754.
Konversi antar tipe angka#
Konversi angka pada Scala terbagi menjadi dua arah. Konversi menaik (widening), misalnya dari Int ke Long, dilakukan compiler secara otomatis ketika tipe tujuan dinyatakan. Sebaliknya, konversi menurun (narrowing) harus diminta secara eksplisit melalui metode toInt, toLong, atau toDouble, karena hilangnya nilai tidak boleh terjadi tanpa diketahui.
Simpan program berikut sebagai konversi-angka.scala.
// Konversi di antara tipe angka
@main def konversiAngka(): Unit =
val skor: Int = 91
val jangkauan: Long = skor
val skorLong: Long = skor.toLong
val skorDouble: Double = skor.toDouble
val rataRata: Double = 86.75
println("Int ke Long, otomatis : " + jangkauan)
println("Int ke Long, toLong : " + skorLong)
println("Int ke Double : " + skorDouble)
println("86.75 ke Int : " + rataRata.toInt)
println("87.9 ke Int : " + 87.9.toInt)
println("-87.9 ke Int : " + (-87.9).toInt)
println("Int.MaxValue + 1 : " + (Int.MaxValue + 1))Jalankan program tersebut.
# mengompilasi dan menjalankan program konversi angka
scala-cli run konversi-angka.scalaInt ke Long, otomatis : 91
Int ke Long, toLong : 91
Int ke Double : 91.0
86.75 ke Int : 86
87.9 ke Int : 87
-87.9 ke Int : -87
Int.MaxValue + 1 : -2147483648Dua baris pertama menghasilkan nilai yang sama, yaitu 91, karena konversi otomatis maupun toLong sama-sama memindahkan nilai dari Int ke Long tanpa kehilangan data. Perbedaannya ada pada kejelasan maksud, dan metode eksplisit lebih disukai ketika konversi merupakan bagian dari logika program.
Tiga baris berikutnya memperlihatkan sifat konversi menurun. Metode toInt memotong bagian pecahan ke arah nol, sehingga 86.75 menjadi 86, 87.9 menjadi 87, dan -87.9 menjadi -87. Perhatikan bahwa hasilnya bukan pembulatan, karena -87.9 tidak menjadi -88. Apabila pembulatan yang diinginkan, gunakan metode round yang disediakan pada tipe angka.
Baris terakhir menunjukkan akibat jangkauan yang terbatas. Penjumlahan Int.MaxValue + 1 tidak menghasilkan bilangan yang lebih besar, melainkan berputar ke batas bawah tipe Int, yaitu -2147483648. Perilaku ini disebut overflow. Apabila nilai yang dihitung berpotensi melewati jangkauan Int, konversikan operand ke Long terlebih dahulu sebelum aritmetika dilakukan.
Diagram berikut merangkum arah konversi di antara ketiga tipe angka.
Unit dan Nothing#
Tipe Unit mewakili ketiadaan hasil yang berguna. Tipe ini memiliki tepat satu nilai, yaitu (), dan menjadi tipe kembalian bagi setiap definisi yang bergantung pada efek sampingnya (side effect), bukan pada nilainya. Semua program pada modul ini ditulis sebagai @main def ... : Unit =, dan penulisan itu kini dapat dipahami sepenuhnya: prosedurnya tidak mengembalikan apa pun yang layak digunakan.
Program berikut memperlihatkan nilai () secara langsung. Simpan sebagai tipe-unit.scala.
// Unit memiliki tepat satu nilai, yaitu ()
@main def tipeUnit(): Unit =
val hasil = println("Menyusun arsip")
println("println mengembalikan: " + hasil)Jalankan program tersebut.
# mengompilasi dan menjalankan program tipe unit
scala-cli run tipe-unit.scalaMenyusun arsip
println mengembalikan: ()Prosedur println mencetak barisnya, lalu mengembalikan nilai Unit yang tersimpan di hasil. Pada baris kedua, nilai itu dicetak sebagai (). Analog terdekat Unit adalah void pada Java, tetapi keduanya tidak identik, karena Unit adalah tipe sungguhan dengan satu nilai yang bahkan dapat disimpan ke dalam val.
Tipe Nothing berada di ujung lain hierarki. Tipe ini tidak memiliki satu nilai pun, sehingga tidak ada ekspresi yang dapat menghasilkannya. Perannya adalah menjadi subtipe dari semua tipe lain, sebuah posisi yang dimanfaatkan compiler saat menyimpulkan tipe gabungan, misalnya ketika satu cabang ekspresi selalu melempar eksepsi melalui throw. Daftar kosong bawaan pustaka koleksi, Nil, juga bertipe List[Nothing], sehingga daftar kosong yang sama dapat digunakan sebagai nilai awal untuk daftar dengan tipe elemen apa pun. Karena tidak ada nilai yang bisa dibuat, Nothing paling mudah dipahami sebagai tipe penanda pada definisi dan pesan compiler, bukan sebagai tipe yang diinstansiasi.
Kesalahan umum#
Tiga kesalahan berikut sering muncul ketika tipe angka baru mulai digunakan.
- Menghitung dengan
Intuntuk nilai yang berpotensi besar. Batas atas Int hanya 2147483647, sehingga operasi seperti jumlah penduduk atau hasil perkalian besar sebaiknya disimpan dan dihitung denganLong, yang diperoleh dengan akhiranLatau metodetoLong. - Menganggap
toIntmembulatkan nilai pecahan. Metode ini memotong ke arah nol, sehingga-87.9.toIntmenghasilkan -87 dan bukan -88. Gunakanroundapabila pembulatan yang dimaksud. - Membandingkan
Doubledengan tanda sama dengan untuk nilai uang. Perkiraan biner padaDoublemembuat perbandingan seperti itu tidak andal. Gunakan satuan terkecil dalam tipe bulat atauBigDecimal.
Ringkasan#
- Tipe dasar yang paling sering digunakan adalah Int 32 bit, Long 64 bit dengan akhiran
L, Double pecahan presisi ganda, Boolean, dan Unit. - Double menyimpan perkiraan biner, sehingga tidak tepat untuk nilai uang dan perbandingan desimal yang presisi.
- Konversi menaik dari Int ke Long atau Double berjalan otomatis, sedangkan konversi menurun memerlukan
toInt,toLong, atautoDoubledengan hasil yang dipotong ke arah nol. - Unit memiliki satu nilai
(), sedangkan Nothing tidak memiliki nilai sama sekali dan menjadi subtipe dari semua tipe.
Referensi#
- Tour of Scala: Unified Types — Scala Center — Menggambarkan hierarki tipe Scala sampai Unit dan Nothing.
- Scala 3 Book: A first look at Scala types — Scala Center — Memperkenalkan tipe dasar Scala dan peran compiler dalam menentukan tipe.