Dirilis resmi pada tahun 2016, Kotlin telah menarik banyak perhatian dalam beberapa tahun terakhir, terutama sejak Google mengumumkan dukungannya terhadap Kotlin sebagai alternatif Java pada platform Android. Dengan keputusan yang baru-baru ini diumumkan untuk menjadikan Kotlin sebagai bahasa pilihan untuk Android, Anda mungkin bertanya-tanya apakah sudah waktunya untuk mulai mempelajari bahasa pemrograman baru. Jika demikian, artikel ini dapat membantu Anda memutuskan.
Sejarah rilis Kotlin
Kotlin diumumkan pada tahun 2011, tetapi rilis stabil pertama, versi 1.0, tidak muncul hingga 2016. Bahasa ini gratis dan bersumber terbuka, dikembangkan oleh JetBrains dengan Andrey Breslav sebagai perancang bahasa utamanya. Kotlin 1.3.40 dirilis pada Juni 2019.
Tentang Kotlin
Kotlin adalah bahasa pemrograman modern dengan tipe statis yang menampilkan konstruksi pemrograman berorientasi objek dan fungsional. Ini menargetkan beberapa platform, termasuk JVM, dan sepenuhnya dapat dioperasikan dengan Java. Dalam banyak hal, Kotlin akan terlihat seperti Java jika dirancang saat ini. Pada artikel ini saya memperkenalkan delapan fitur Kotlin yang saya yakin pengembang Java akan senang untuk menemukannya.
- Bersih, sintaks yang kompak
- Sistem tipe tunggal (hampir)
- Keamanan nol
- Fungsi dan pemrograman fungsional
- Kelas data
- Ekstensi
- Operator kelebihan beban
- Objek tingkat atas dan pola Singleton
Halo Dunia! Kotlin versus Java
Kode 1 menunjukkan pesan "Halo, dunia!" fungsi yang ditulis di Kotlin.
Daftar 1. "Halo, dunia!" di Kotlin
fun main() { println("Hello, world!") }
Sesederhana itu, contoh ini mengungkapkan perbedaan utama dari Java.
main
adalah fungsi tingkat atas; artinya, fungsi Kotlin tidak perlu disarangkan di dalam kelas.- Tidak ada
public static
pengubah. Meskipun Kotlin memiliki pengubah visibilitas, defaultnya adalahpublic
dan dapat dihilangkan. Kotlin juga tidak mendukungstatic
pengubah, tetapi tidak diperlukan dalam kasus ini karenamain
merupakan fungsi tingkat atas. - Sejak Kotlin 1.3, parameter array-of-strings untuk
main
tidak diperlukan dan dapat dihilangkan jika tidak digunakan. Jika perlu, itu akan dideklarasikan sebagaiargs : Array
. - Tidak ada tipe pengembalian yang ditentukan untuk fungsi tersebut. Jika Java menggunakan
void
, Kotlin menggunakanUnit
, dan jika tipe kembalian dari suatu fungsi adalahUnit
, ia dapat dihilangkan. - Tidak ada titik koma dalam fungsi ini. Di Kotlin, titik koma bersifat opsional, dan oleh karena itu jeda baris bersifat signifikan.
Itu adalah ringkasannya, tapi masih banyak yang harus dipelajari tentang perbedaan Kotlin dari Java dan, dalam banyak kasus, memperbaikinya.
1. Sintaks yang lebih rapi dan lebih ringkas
Java sering dikritik karena terlalu bertele-tele, tetapi beberapa verbositas dapat menjadi teman Anda, terutama jika itu membuat kode sumber lebih mudah dipahami. Tantangan dalam desain bahasa adalah mengurangi verbositas sambil tetap mempertahankan kejelasan, dan menurut saya Kotlin dapat mengatasi tantangan ini.
Seperti yang Anda lihat di Listing 1, Kotlin tidak memerlukan titik koma, dan memungkinkan penghilangan tipe kembalian untuk Unit
fungsi. Mari pertimbangkan beberapa fitur lain yang membantu membuat Kotlin menjadi alternatif yang lebih rapi dan ringkas untuk Java.
Ketik inferensi
Di Kotlin, Anda dapat mendeklarasikan variabel sebagai var x : Int = 5
, atau Anda dapat menggunakan versi yang lebih pendek tetapi sama jelasnya var x = 5
. (Meskipun Java sekarang mendukung var
deklarasi, fitur tersebut tidak muncul hingga Java 10, lama setelah fitur tersebut muncul di Kotlin.)
Kotlin juga memiliki val
deklarasi untuk variabel hanya-baca, yang serupa dengan variabel Java yang telah dideklarasikan sebagai final
, artinya variabel tersebut tidak dapat ditetapkan kembali. Daftar 2 memberi contoh.
Kode 2. Variabel hanya-baca di Kotlin
val x = 5 ... x = 6 // ERROR: WILL NOT COMPILE
Properti versus bidang
Where Java has fields, Kotlin has properties. Properties are declared and accessed in a manner similar to public fields in Java, but Kotlin provides default implementations of accessor/mutator functions for properties; that is, Kotlin provides get()
functions for val
properties and both get()
and set()
functions for var
properties. Customized versions of get()
and set()
can be implemented when necessary.
Most properties in Kotlin will have backing fields, but it is possible to define a computed property, which is essentially a get()
function without a backing field. For example, a class representing a person might have a property for dateOfBirth
and a computed property for age
.
Default versus explicit imports
Java implicitly imports classes defined in package java.lang
, but all other classes must be explicitly imported. As a result, many Java source files start by importing collection classes from java.util
, I/O classes from java.io
, and so forth. By default, Kotlin implicitly imports kotlin.*
, which is roughly analogous to Java importing java.lang.*
, but Kotlin also imports kotlin.io.*
, kotlin.collections.*
, and classes from several other packages. Because of this, Kotlin source files normally require fewer explicit imports than Java source files, especially for classes that use collections and/or standard I/O.
No call to 'new' for constructors
In Kotlin, the keyword new
is not needed to create a new object. To call a constructor, just use the class name with parentheses. The Java code
Student s = new Student(...); // or var s = new Student(...);
could be written as follows in Kotlin:
var s = Student(...)
String templates
Strings can contain template expressions, which are expressions that are evaluated with results inserted into the string. A template expression starts with a dollar sign ($) and consists of either a simple name or an arbitrary expression in curly braces. String templates can shorten string expressions by reducing the need for explicit string concatenation. As an example, the following Java code
println("Name: " + name + ", Department: " + dept);
could be replaced by the shorter but equivalent Kotlin code.
println("Name: $name, Department: $dept")
Extends and implements
Java programmers know that a class can extend
another class and implement
one or more interfaces. In Kotlin, there is no syntactic difference between these two similar concepts; Kotlin uses a colon for both. For example, the Java code
public class Student extends Person implements Comparable
would be written more simply in Kotlin as follows:
class Student : Person, Comparable
No checked exceptions
Kotlin supports exceptions in a manner similar to Java with one big difference–Kotlin does not have checked exceptions. While they were well intentioned, Java's checked exceptions have been widely criticized. You can still throw
and catch
exceptions, but the Kotlin compiler does not force you to catch any of them.
Destructuring
Think of destructuring as a simple way of breaking up an object into its constituent parts. A destructuring declaration creates multiple variables at once. Listing 3 below provides a couple of examples. For the first example, assume that variable student
is an instance of class Student
, which is defined in Listing 12 below. The second example is taken directly from the Kotlin documentation.
Listing 3. Destructuring examples
val (_, lName, fName) = student // extract first and last name from student object // underscore means we don't need student.id for ((key, value) in map) { // do something with the key and the value }
'if' statements and expressions
In Kotlin, if
can be used for control flow as with Java, but it can also be used as an expression. Java's cryptic ternary operator (?:
) is replaced by the clearer but somewhat longer if
expression. For example, the Java code
double max = x >= y ? x : y
would be written in Kotlin as follows:
val max = if (x >= y) then x else y
Kotlin is slightly more verbose than Java in this instance, but the syntax is arguably more readable.
'when' replaces 'switch'
My least favorite control structure in C-like languages is the switch
statement. Kotlin replaces the switch
statement with a when
statement. Listing 4 is taken straight from the Kotlin documentation. Notice that break
statements are not required, and you can easily include ranges of values.
Listing 4. A 'when' statement in Kotlin
when (x) { in 1..10 -> print("x is in the range") in validNumbers -> print("x is valid") !in 10..20 -> print("x is outside the range") else -> print("none of the above") }
Try rewriting Listing 4 as a traditional C/Java switch
statement, and you will get an idea of how much better off we are with Kotlin's when
statement. Also, similar to if
, when
can be used as an expression. In that case, the value of the satisfied branch becomes the value of the overall expression.
Switch expressions in Java
Java 12 introduced switch expressions. Similar to Kotlin's when
, Java's switch expressions do not require break
statements, and they can be used as statements or expressions. See "Loop, switch, or take a break? Deciding and iterating with statements" for more about switch expressions in Java.
2. Single type system (almost)
Java has two separate type systems, primitive types and reference types (a.k.a., objects). There are many reasons why Java includes two separate type systems. Actually that's not true. As outlined in my article A case for keeping primitives in Java, there is really only one reason for primitive types--performance. Similar to Scala, Kotlin has only one type system, in that there is essentially no distinction between primitive types and reference types in Kotlin. Kotlin uses primitive types when possible but will use objects if necessary.
So why the caveat of "almost"? Because Kotlin also has specialized classes to represent arrays of primitive types without the autoboxing overhead: IntArray
, DoubleArray
, and so forth. On the JVM, DoubleArray
is implemented as double[]
. Does using DoubleArray
really make a difference? Let's see.
Benchmark 1: Matrix multiplication
In making the case for Java primitives, I showed several benchmark results comparing Java primitives, Java wrapper classes, and similar code in other languages. One of the benchmarks was simple matrix multiplication. To compare Kotlin performance to Java, I created two matrix multiplication implementations for Kotlin, one using Array
and one using Array
. Listing 5 shows the Kotlin implementation using Array
.
Listing 5. Matrix multiplication in Kotlin
fun multiply(a : Array, b : Array) : Array { if (!checkArgs(a, b)) throw Exception("Matrices are not compatible for multiplication") val nRows = a.size val nCols = b[0].size val result = Array(nRows, {_ -> DoubleArray(nCols, {_ -> 0.0})}) for (rowNum in 0 until nRows) { for (colNum in 0 until nCols) { var sum = 0.0 for (i in 0 until a[0].size) sum += a[rowNum][i]*b[i][colNum] result[rowNum][colNum] = sum } } return result }
Selanjutnya, saya membandingkan kinerja dua versi Kotlin dengan Java double
dan Java dengan Double
, menjalankan keempat tolok ukur pada laptop saya saat ini. Karena ada sejumlah kecil "gangguan" dalam menjalankan setiap benchmark, saya menjalankan semua versi tiga kali dan rata-rata hasilnya, yang dirangkum dalam Tabel 1.
Tabel 1. Kinerja runtime benchmark perkalian matriks
Hasil berwaktu (dalam detik)Jawa ( |
Jawa ( |
Kotlin ( |
Kotlin ( |
7.30 | 29.83 | 6.81 | 15.82 |
I was somewhat surprised by these results, and I draw two takeaways. First, Kotlin performance using DoubleArray
is clearly superior to Kotlin performance using Array
, which is clearly superior to that of Java using the wrapper class Double
. And second, Kotlin performance using DoubleArray
is comparable to--and in this example slightly better than--Java performance using the primitive type double
.
Clearly Kotlin has done a great job of optimizing away the need for separate type systems--with the exception of the need to use classes like DoubleArray
instead of Array
.
Benchmark 2: SciMark 2.0
My article on primitives also included a second, more scientific benchmark known as SciMark 2.0, which is a Java benchmark for scientific and numerical computing available from the National Institute of Standards and Technology (NIST). The SciMark benchmark measures performance of several computational routines and reports a composite score in approximate Mflops (millions of floating point operations per second). Thus, larger numbers are better for this benchmark.
Dengan bantuan IntelliJ IDEA, saya mengonversi benchmark SciMark versi Java ke Kotlin. IntelliJ IDEA secara otomatis dikonversi double[]
dan int[]
di Java ke DoubleArray
dan IntArray
di Kotlin. Saya kemudian membandingkan versi Java yang menggunakan primitif dengan versi Kotlin yang menggunakan DoubleArray
dan IntArray
. Seperti sebelumnya, saya menjalankan kedua versi tiga kali dan rata-rata hasilnya, yang dirangkum dalam Tabel 2. Sekali lagi tabel menunjukkan hasil yang kira-kira sebanding.
Tabel 2. Kinerja runtime benchmark SciMark
Performa (dalam Mflops)Jawa | Kotlin |
1818.22 | 1815,78 |