Menu
CoddyTech

Hàm mở rộng

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

Hàm mở rộng thêm một hàm vào kiểu dữ liệu đã có mà không cần thay đổi mã nguồn của kiểu đó. Tên hàm được viết sau kiểu nhận, bên trong hàm, this tham chiếu đến kiểu nhận và bạn cũng có thể lược bỏ từ khóa này:

fun String.shout() = uppercase() + "!"
fun Int.isEven() = this % 2 == 0

Bên trong main:

println("hello".shout())
println(7.isEven())
println(listOf(1, 2, 3, 4).count { it.isEven() })

Đầu ra:

HELLO!
false
2

Extension cũng hoạt động với các lớp của bạn, và thuộc tính mở rộng bổ sung các thuộc tính được tính toán. Extension không thể bổ sung trạng thái được lưu trữ và chỉ truy cập được các thành viên public của đối tượng nhận:

class Temperature(val celsius: Double)

val Temperature.fahrenheit: Double
    get() = celsius * 9 / 5 + 32

fun Temperature.describe() = "$celsius C is $fahrenheit F"

Bên trong main:

println(Temperature(100.0).describe())

Đầu ra:

100.0 C is 212.0 F

Các phần mở rộng được phân giải theo cách tĩnh: kiểu được khai báo của biểu thức quyết định phần mở rộng nào được chọn khi chương trình biên dịch, chứ không phải lớp của đối tượng. Đây là điểm khác biệt chính so với thành viên được ghi đè:

open class Shape {
    open fun member() = "shape member"
}
class Circle : Shape() {
    override fun member() = "circle member"
}

fun Shape.ext() = "shape extension"
fun Circle.ext() = "circle extension"

Bên trong main:

val s: Shape = Circle()
println(s.member())
println(s.ext())

Đầu ra:

circle member
shape extension

Khi lớp đã có một thành viên cùng chữ ký, thành viên đó luôn được ưu tiên. Hãy dùng thành viên cho hành vi thuộc về kiểu và có thể được ghi đè; dùng hàm mở rộng cho các tiện ích được thêm từ bên ngoài, chẳng hạn như định dạng cho một lớp mà bạn không sở hữu:

class Robot {
    fun greet() = "member greet"
}

fun Robot.greet() = "extension greet"
fun Robot.wave() = "extension wave"

Bên trong main:

val r = Robot()
println(r.greet())
println(r.wave())

Đầu ra:

member greet
extension wave
challenge icon

Thử thách

Dễ

Money(cents) đã có sẵn trong Money.kt. Hãy viết các phần mở rộng cho nó trong MoneyExtensions.kt: các thuộc tính mở rộng Int.dollars và Int.cents, chuyển một số thành Money (12.dollars là 1200 xu), Money.format(), trả về $12.50 (hoặc -$3.05 với số tiền âm), List<Money>.total(), trả về tổng dưới dạng Money, và Money.split(parts), chia thành parts khoản tiền chênh nhau tối đa một xu, với các khoản lớn hơn đứng trước.

Code được cung cấp đọc các dòng 12 dollars hoặc 50 cents và dòng cuối cùng có số phần. Code in ra từng khoản tiền, total: $12.50 và split 3: $4.17 $4.17 $4.16.

Code của bạn nằm trong MoneyExtensions.kt và Money.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()
    val amounts = input.dropLast(1).map { line ->
        val (n, unit) = line.split(" ")
        if (unit == "dollars") n.toInt().dollars else n.toInt().cents
    }
    for (m in amounts) println(m.format())
    val total = amounts.total()
    println("total: ${total.format()}")
    val parts = input.last().toInt()
    println("split $parts: " + total.split(parts).joinToString(" ") { it.format() })
}
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