cv.cm

Lập trình · Ngôn ngữ lập trình

Học Kotlin cơ bản cho người mới bắt đầu

8 phút đọc

Trả lời nhanh

Kotlin là ngôn ngữ hiện đại, kiểu tĩnh do JetBrains phát triển, chạy trên JVM và là ngôn ngữ ưu tiên cho phát triển Android. Kotlin ngắn gọn, tương thích hoàn toàn với Java, và hệ thống kiểu giúp ngăn phần lớn lỗi con trỏ null ngay lúc biên dịch. Trang này trình bày cài đặt, biến, null safety, cấu trúc điều khiển, hàm, collection, lớp, coroutine và xử lý lỗi bằng các ví dụ chạy được.

Kotlin là gì và dùng để làm gì

Kotlin là ngôn ngữ lập trình đa dụng, kiểu tĩnh do JetBrains tạo ra. Kotlin chạy trên Java Virtual Machine (JVM), đồng thời có thể biên dịch sang JavaScript, WebAssembly và mã máy native. Kotlin tương thích hoàn toàn với Java: một tệp Kotlin có thể gọi mọi thư viện Java, và mã Java cũng có thể gọi Kotlin.

Từ năm 2019, Google khuyến nghị Kotlin là ngôn ngữ ưu tiên cho ứng dụng Android, nên phần lớn mã Android mới đều viết bằng Kotlin. Kotlin cũng phổ biến ở phía máy chủ với các framework như Spring Boot và Ktor, còn Kotlin Multiplatform cho phép các nhóm dùng chung logic nghiệp vụ giữa Android, iOS, desktop và web.

Cài đặt Kotlin và chạy Hello, world

Cách dễ nhất để bắt đầu là dùng IntelliJ IDEA hoặc Android Studio, vì cả hai đã tích hợp sẵn Kotlin. Với dòng lệnh, hãy cài trình biên dịch (ví dụ qua SDKMAN! hoặc Homebrew) và môi trường chạy Java (JDK). Chương trình Kotlin bắt đầu từ hàm main ở cấp cao nhất; không cần khai báo lớp.

// hello.kt
fun main() {
    println("Hello, world!")
}
kotlinc hello.kt -include-runtime -d hello.jar
java -jar hello.jar
# Hello, world!

Với dự án thực tế, công cụ build như Gradle (dùng Kotlin DSL) là lựa chọn thông thường. Bạn cũng có thể chạy thử mã mà không cần cài gì trên Kotlin Playground trực tuyến.

Biến và các kiểu dữ liệu cơ bản

Khai báo biến chỉ đọc bằng val và biến có thể thay đổi bằng var. Trình biên dịch tự suy ra kiểu, nhưng bạn cũng có thể ghi kiểu sau dấu hai chấm. Nên ưu tiên val: mã sẽ dễ suy luận hơn.

val name = "Ada"          // String, cannot be reassigned
var count: Int = 0        // explicit type, can change
count += 1
val pi = 3.14159          // Double
val big = 9_000_000_000L  // Long
println("$name has $count item(s), pi is ${"%.2f".format(pi)}")
// Ada has 1 item(s), pi is 3.14
KiểuVí dụGhi chú
Int42số nguyên có dấu 32 bit
Long42Lsố nguyên có dấu 64 bit
Double3.14số thực dấu phẩy động 64 bit
Booleantruetrue or false
Char'k'một ký tự đơn
String"text"bất biến; hỗ trợ mẫu $name

Kotlin không tự động mở rộng kiểu số: Int không được tự chuyển thành Long. Hãy gọi tường minh toLong(), toDouble() hoặc toString().

Null safety (an toàn null)

Null safety là tính năng đặc trưng của Kotlin. Một kiểu thông thường như String không bao giờ chứa được null. Để cho phép null, bạn thêm ?, như String?, và khi đó trình biên dịch buộc bạn xử lý trường hợp null trước khi dùng giá trị.

fun greet(name: String?) {
    // println(name.length)        // compile error: name may be null
    println(name?.length)           // safe call: prints null if name is null
    val len = name?.length ?: 0     // Elvis operator: default value
    if (name != null) {
        println(name.uppercase())   // smart cast: name is String here
    }
    println("length = $len")
}
fun main() {
    greet("kotlin")   // 6, KOTLIN, length = 6
    greet(null)       // null, length = 0
}

Cấu trúc điều khiển: if, when và vòng lặp

Trong Kotlin, if và when là biểu thức nên trả về giá trị. when thay thế câu lệnh switch của Java và C, và có thể so khớp giá trị, khoảng và kiểu.

val score = 82
val grade = if (score >= 50) "pass" else "fail"
val letter = when (score) {
    in 90..100 -> "A"
    in 80..89 -> "B"
    in 70..79 -> "C"
    else -> "F"
}
println("$grade $letter")         // pass B
for (i in 1..3) print("$i ")      // 1 2 3
println()
for (i in 10 downTo 0 step 5) print("$i ")  // 10 5 0
println()
var n = 3
while (n > 0) { print(n); n-- }   // 321
println()

1..3 là khoảng đóng gồm cả hai đầu; 0 until 3 (hoặc 0..<3) loại trừ điểm cuối, thường là điều bạn cần khi duyệt chỉ số.

Hàm và lambda

Hàm được khai báo bằng fun. Tham số có thể có giá trị mặc định, nơi gọi có thể dùng đối số có tên, và hàm có thân là một biểu thức có thể viết bằng dấu =.

fun area(width: Double, height: Double = 1.0): Double = width * height
fun main() {
    println(area(3.0, 2.0))          // 6.0
    println(area(width = 4.0))       // 4.0 (default height)
    val square = { x: Int -> x * x } // a lambda stored in a variable
    println(square(5))               // 25
    val nums = listOf(1, 2, 3, 4, 5)
    val evensSquared = nums.filter { it % 2 == 0 }.map { it * it }
    println(evensSquared)            // [4, 16]
}

Khi lambda có một tham số, bạn có thể gọi nó là it. Khi đối số cuối của hàm là lambda, nó có thể đặt ngoài dấu ngoặc đơn, nên filter { ... } đọc giống một khối lệnh có sẵn.

Collection và data class

Thư viện chuẩn tách collection chỉ đọc và collection có thể thay đổi: listOf, setOf và mapOf tạo collection chỉ đọc, còn mutableListOf, mutableSetOf và mutableMapOf thì có thể sửa.

val fruits = mutableListOf("apple", "banana")
fruits.add("cherry")
val ages = mapOf("Ann" to 31, "Bo" to 25)
println(fruits.size)              // 3
println(ages["Ann"])              // 31
println(ages["Zed"])              // null (map lookups are nullable)
data class User(val name: String, val age: Int)
val u = User("Ann", 31)
val older = u.copy(age = 32)
println(u)                        // User(name=Ann, age=31)
println(u == User("Ann", 31))     // true (structural equality)
val (n, a) = older                // destructuring
println("$n $a")                  // Ann 32

data class tự động có equals, hashCode, toString, copy và khả năng tách giá trị (destructuring). Trong Kotlin, == so sánh nội dung (gọi equals); === so sánh tham chiếu.

Lớp, hàm mở rộng và coroutine

Lớp mặc định là final; đánh dấu open để cho phép kế thừa. Hàm mở rộng (extension function) thêm phương thức vào kiểu có sẵn mà không sửa kiểu đó, còn lớp sealed mô hình hóa một tập trường hợp đóng để when kiểm tra đầy đủ.

fun String.shout(): String = uppercase() + "!"
sealed class Shape
data class Circle(val r: Double) : Shape()
data class Rect(val w: Double, val h: Double) : Shape()
fun area(s: Shape): Double = when (s) {
    is Circle -> Math.PI * s.r * s.r
    is Rect -> s.w * s.h
}   // no else needed: the compiler knows all cases
fun main() {
    println("hi".shout())                   // HI!
    println(area(Rect(2.0, 3.0)))           // 6.0
}

Với mã bất đồng bộ và đồng thời, Kotlin dùng coroutine từ thư viện kotlinx.coroutines. Hàm suspend có thể tạm dừng mà không chặn luồng, và các builder như launch và async khởi chạy coroutine trong một scope. Đây là cách ứng dụng Android gọi mạng ngoài luồng chính.

Xử lý lỗi

Kotlin có try, catch và finally như Java, nhưng mọi ngoại lệ đều là unchecked: bạn không bao giờ bị buộc phải khai báo hay bắt chúng. try cũng là một biểu thức. Với các lỗi có thể đoán trước, thư viện chuẩn có runCatching và Result, và các hàm như toIntOrNull() trả về null thay vì ném ngoại lệ.

fun parse(s: String): Int = try {
    s.toInt()
} catch (e: NumberFormatException) {
    -1
}
fun main() {
    println(parse("42"))              // 42
    println(parse("x"))               // -1
    println("7".toIntOrNull())        // 7
    println("seven".toIntOrNull())    // null
    val r = runCatching { "abc".toInt() }
    println(r.isFailure)              // true
}

Một chương trình hoàn chỉnh nhỏ: đếm tần suất từ

Chương trình này đếm số lần mỗi từ xuất hiện trong văn bản và in ra ba từ đứng đầu. Nó dùng hàm mở rộng chuỗi, các hàm collection và một data class.

data class WordCount(val word: String, val count: Int)
fun topWords(text: String, n: Int): List<WordCount> =
    text.lowercase()
        .split(Regex("[^a-z']+"))
        .filter { it.isNotBlank() }
        .groupingBy { it }
        .eachCount()
        .map { (w, c) -> WordCount(w, c) }
        .sortedWith(compareByDescending<WordCount> { it.count }.thenBy { it.word })
        .take(n)
fun main() {
    val text = "The cat and the dog. The dog sat, and the cat ran."
    for ((word, count) in topWords(text, 3)) {
        println("$word: $count")
    }
}
// the: 4
// and: 2
// cat: 2

Học tiếp ở đâu

  • Tài liệu chính thức của Kotlin tại kotlinlang.org, bắt đầu từ Basic syntax và Kotlin tour.
  • Kotlin Koans, bộ bài tập nhỏ chạy ngay trong trình duyệt.
  • Hướng dẫn Android Developers về Kotlin trên Android, hoặc hướng dẫn Ktor và Spring cho Kotlin phía máy chủ.
  • Hướng dẫn kotlinx.coroutines khi bạn cần viết mã bất đồng bộ.

Câu hỏi thường gặp

Kotlin có tốt hơn Java không?

Kotlin ngắn gọn hơn và bổ sung null safety, data class và coroutine, nhưng chạy trên cùng JVM và dùng chung thư viện. Nhiều nhóm trộn cả hai ngôn ngữ trong một dự án, nên lựa chọn nằm ở phong cách và năng suất chứ không phải khả năng.

Có cần học Java trước khi học Kotlin không?

Không. Bạn có thể học Kotlin như ngôn ngữ JVM đầu tiên. Biết chút Java sẽ giúp khi đọc mã Android cũ hoặc tài liệu thư viện Java.

val và var khác nhau thế nào?

val khai báo tham chiếu chỉ đọc, không thể gán lại sau khi khởi tạo, còn var khai báo tham chiếu có thể gán lại. Một val trỏ tới danh sách có thể thay đổi vẫn cho phép sửa nội dung danh sách đó.

Kotlin có dùng để làm ứng dụng iOS được không?

Có, thông qua Kotlin Multiplatform, biên dịch mã Kotlin dùng chung thành mã native cho iOS. Giao diện người dùng thường vẫn viết bằng Swift, dù Compose Multiplatform cũng có thể dùng chung mã UI.

Toán tử ?: trong Kotlin làm gì?

Đó là toán tử Elvis: a ?: b trả về a nếu a không null, ngược lại trả về b. Nó thường kết hợp với safe call, như name?.length ?: 0.

Tất cả hướng dẫn lập trình