Menu
CoddyTech

Hằng số với const

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 14 trên 57.

const val khai báo một hằng số tại thời điểm biên dịch: giá trị cố định khi chương trình được biên dịch. Bạn có thể khai báo hằng số này ở cấp cao nhất của tệp và bên trong object hoặc companion object, chỉ áp dụng cho các kiểu cơ bản như Int, Double và Boolean, cũng như String:

const val MAX_PLAYERS = 4

class Lobby {
    val players = mutableListOf<String>()

    fun canStart() = players.size in MIN_PLAYERS..MAX_PLAYERS

    companion object {
        const val MIN_PLAYERS = 2
    }
}

Bên trong main:

val lobby = Lobby()
lobby.players += listOf("ada", "bo")
println(lobby.canStart())
println("${Lobby.MIN_PLAYERS}-$MAX_PLAYERS players")

Đầu ra:

true
2-4 players

Hằng số được đặt tên theo dạng UPPER_SNAKE_CASE. const có quy tắc nghiêm ngặt hơn val: giá trị phải xác định được mà không cần chạy code, vì vậy không thể dùng lời gọi hàm hoặc danh sách. val trong một đối tượng cũng được chia sẻ và chỉ đọc, đồng thời chấp nhận mọi giá trị:

const val GREETING = "Hello"                  // một giá trị chuỗi String viết trực tiếp
const val TIMEOUT_MS = 30 * 1000              // tính từ các giá trị viết trực tiếp
// const val START = System.nanoTime()        // lỗi: không phải hằng số
// const val DAYS = listOf("Mon", "Tue")      // lỗi: chỉ chấp nhận các kiểu dữ liệu cơ bản và String
object Defaults {
    val DAYS = listOf("Mon", "Tue")           // một val chỉ đọc dùng chung
}

Đối tượng companion cũng có thể lưu giữ trạng thái có thể thay đổi dùng chung. Mỗi instance đọc và thay đổi cùng một thuộc tính, rất phù hợp với bộ đếm và bộ nhớ đệm. Lớp này giữ một instance cho mỗi tên, vì vậy gọi hai lần sẽ trả về cùng một đối tượng (=== kiểm tra xem hai tham chiếu có trỏ đến cùng một đối tượng hay không):

class Tile private constructor(val name: String) {
    companion object {
        private val cache = mutableMapOf<String, Tile>()
        val created: Int
            get() = cache.size

        fun of(name: String) = cache.getOrPut(name) { Tile(name) }
    }
}

Bên trong main:

val a = Tile.of("grass")
val b = Tile.of("grass")
Tile.of("water")
println(a === b)
println(Tile.created)

Đầu ra:

true
2

Cấp cao nhất hay companion? Một hằng số hoặc hàm cấp cao nhất thuộc về tệp của nó và được dùng bằng tên thuần trong mọi tệp của gói. Đặt một hằng số trong companion khi nó thuộc về một lớp: nơi gọi viết Lobby.MIN_PLAYERS, cho biết giá trị này đến từ đâu, còn private const val chỉ nằm trong lớp đó.

// Limits.kt
const val MAX_UPLOAD_MB = 25                 // dùng ở mọi nơi dưới tên MAX_UPLOAD_MB

// Lobby.kt
class Lobby {
    companion object {
        const val MIN_PLAYERS = 2            // dùng dưới tên Lobby.MIN_PLAYERS
        private const val ROOM_PREFIX = "L-" // chỉ dùng trong Lobby
    }
}
challenge icon

Thử thách

Dễ

Hoàn thiện lớp (class) Order(val item: String, val qty: Int). Đối tượng companion của lớp chứa các hằng số MAX_QTY = 10 và PREFIX = "ORD", một bộ đếm riêng cho số đơn hàng tiếp theo, tổng dùng chung chỉ đọc totalItems và count tính toán số đơn hàng đã tạo. Khối init yêu cầu số lượng nằm trong khoảng từ 1 đến MAX_QTY, kèm thông báo quantity must be 1-10 (được tạo từ hằng số), sau đó gán mã định danh cho đơn hàng lần lượt là ORD-001, ORD-002, ... và cộng số lượng của đơn hàng vào totalItems. label() trả về ORD-001 tea x2.

Code được cung cấp in ra max per order: 10, sau đó đọc các dòng tea 2 và in nhãn hoặc thông báo lỗi, cuối cùng in ra orders: 2, items: 5. Đơn hàng bị từ chối không làm mất số thứ tự.

Viết code của bạn trong Order.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ệp này.

Tự làm thử

fun main() {
    // Đoạn code đầu vào/đầu ra có sẵn: giữ nguyên
    val input = generateSequence(::readLine).toList()
    println("max per order: ${Order.MAX_QTY}")
    for (line in input) {
        val (item, qty) = line.split(" ")
        try {
            println(Order(item, qty.toInt()).label())
        } catch (e: IllegalArgumentException) {
            println(e.message)
        }
    }
    println("orders: ${Order.count}, items: ${Order.totalItems}")
}
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