Menu
CoddyTech

lazy và lateinit

Thuộc phần Lập trình hướng đối tượng trong hành trình Kotlin của Coddy. Bài học 10 trên 57.

by lazy { ... } trì hoãn việc tính toán một val cho đến lần đầu tiên được đọc, sau đó ghi nhớ kết quả. Cách này phù hợp với những giá trị cần nhiều tài nguyên để tính toán hoặc không phải lúc nào cũng cần dùng:

class Report(val rows: List<Int>) {
    val summary: String by lazy {
        println("computing summary")
        "total ${rows.sum()}"
    }
}

Bên trong main:

val r = Report(listOf(3, 4))
println("created")
println(r.summary)
println(r.summary)

Đầu ra:

created
computing summary
total 7
total 7

Khối lệnh chỉ chạy một lần: lần đọc thứ hai ở trên dùng kết quả đã lưu. Khác với getter tùy chỉnh, chạy mỗi lần đọc, lazy chỉ tính một lần rồi lưu vào bộ nhớ đệm:

class Demo {
    var reads = 0
    val viaGetter: Int
        get() { reads++; return 42 }
    val viaLazy: Int by lazy { reads++; 42 }
}

Bên trong main:

val d = Demo()
repeat(3) { d.viaGetter; d.viaLazy }
println(d.reads)

Đầu ra:

4

lateinit var khai báo một thuộc tính không null được gán sau, sau khi khởi tạo, chẳng hạn như bằng một phương thức thiết lập. Đọc thuộc tính trước khi gán sẽ gây ra ngoại lệ, còn ::name.isInitialized dùng để kiểm tra thuộc tính:

class Screen {
    lateinit var title: String

    fun load(t: String) { title = t }
    fun ready() = ::title.isInitialized
}

Bên trong main:

val s = Screen()
println(s.ready())
s.load("Home")
println(s.ready())
println(s.title)

Đầu ra:

false
true
Home

Ưu tiên khởi tạo thông thường khi đã biết giá trị lúc khởi tạo. Dùng lazy cho val tốn nhiều tài nguyên và có thể không cần đến, chỉ dùng lateinit cho var mà giá trị thực sự được gán sau đó; lateinit không dùng được với Int, Boolean hoặc các kiểu nguyên thủy khác.

val config by lazy { loadConfig() }     // tính một lần khi dùng lần đầu
lateinit var user: User                 // gán giá trị sau khi khởi tạo
// lateinit var count: Int              // lỗi: không dùng được với kiểu dữ liệu nguyên thủy
challenge icon

Thử thách

Dễ

Hoàn thiện lớp Document(val text: String). Thêm wordCount làm thuộc tính lazy để đếm số từ (được phân tách bằng dấu cách đơn) và ghi nhận mỗi lần tính bằng cách cộng 1 vào computations, đồng thời thêm lateinit var author: String cùng phương thức credit() trả về by Ada hoặc by unknown khi chưa đặt tác giả.

Code được cung cấp đọc văn bản, đọc wordCount ba lần, đặt tác giả khi có dòng thứ hai và in ra words 5, computed 1, by Ada.

Code của bạn nằm trong Document.kt. Main.kt chứa code đầu vào/đầu ra được cung cấp và bạn không thể chỉnh sửa.

Tự làm thử

fun main() {
    // Đoạn code đầu vào/đầu ra đã cho: giữ nguyên
    val input = generateSequence(::readLine).toList()
    val doc = Document(input[0])
    val reads = List(3) { doc.wordCount }
    val words = reads.first()
    if (input.size > 1) doc.author = input[1]
    println("words $words, computed ${doc.computations}, ${doc.credit()}")
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Playground Kotlin