Gọi phương thức độ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 27 trên 57.
Tính đa hình có nghĩa là một lời gọi có thể hoạt động theo nhiều cách. Khi bạn gọi một phương thức được ghi đè thông qua kiểu lớp cha hoặc kiểu giao diện, Kotlin sẽ kiểm tra lớp thực tế của đối tượng khi chương trình chạy và thực thi phiên bản của lớp đó. Đây là điều phối động:
abstract class Animal(val name: String) {
abstract fun sound(): String
fun speak() = "$name: ${sound()}"
}
class Dog(name: String) : Animal(name) {
override fun sound() = "Woof"
}
class Cow(name: String) : Animal(name) {
override fun sound() = "Moo"
}Bên trong main:
val barn: List<Animal> = listOf(Dog("Rex"), Cow("Bess"), Dog("Fido"))
for (a in barn) println(a.speak())Đầu ra:
Rex: Woof
Bess: Moo
Fido: WoofĐoạn code gọi hàm không thay đổi khi có lớp con mới. totalArea bên dưới được viết để dùng với mọi Shape, nên nó cũng hoạt động với Triangle, dù không ai thay đổi nó:
interface Shape {
fun area(): Double
}
class Square(val side: Double) : Shape {
override fun area() = side * side
}
class Triangle(val base: Double, val height: Double) : Shape {
override fun area() = base * height / 2
}
fun totalArea(shapes: List<Shape>) = shapes.sumOf { it.area() }Bên trong main:
println(totalArea(listOf(Square(2.0), Triangle(3.0, 4.0))))Đầu ra:
10.0Việc điều phối dựa vào đối tượng, không phải biến. Gán một đối tượng khác cho cùng một biến sẽ thay đổi đoạn code chạy ở lần gọi tiếp theo:
interface Mode {
fun format(text: String): String
}
class Normal : Mode {
override fun format(text: String) = text
}
class Shout : Mode {
override fun format(text: String) = text.uppercase() + "!"
}Bên trong main:
var mode: Mode = Normal()
println(mode.format("hello"))
mode = Shout()
println(mode.format("hello"))Đầu ra:
hello
HELLO!Tính đa hình thay thế các bước kiểm tra như if (kind == "dog"), vốn sẽ lặp lại ở mọi nơi sử dụng động vật. Mỗi lớp giữ hành vi riêng, và để thêm một loại động vật, bạn chỉ cần thêm một lớp:
// Không dùng tính đa hình: mỗi hàm (function) đều kiểm tra loại
fun sound(kind: String) = when (kind) { "dog" -> "Woof"; "cow" -> "Moo"; else -> "?" }
// Dùng tính đa hình: loại mới có hành vi riêng
class Sheep(name: String) : Animal(name) {
override fun sound() = "Baa"
}Thử thách
DễCharacter(name, hp) là một lớp mở. takeHit(damage) trừ sát thương khỏi hp (không bao giờ thấp hơn 0), còn status() trả về Ada: 30 hp hoặc Ada: down khi bằng 0. Bạn có thể đọc hp ở mọi nơi, nhưng chỉ có thể thay đổi nó bên trong Character. Hoàn thành các lớp con bằng cách ghi đè takeHit và truyền sát thương đã thay đổi cho super.takeHit: Warrior (40 hp) nhận một nửa sát thương, làm tròn xuống; Mage (25 hp) có lá chắn hấp thụ toàn bộ đòn đánh đầu tiên; Rogue (30 hp) né mọi đòn đánh thứ hai nhận được (lần thứ 2, thứ 4, ...).
Code được cung cấp đọc nhóm nhân vật theo dạng warrior:Ada,mage:Bo,rogue:Cy, sau đó tấn công bằng all 10 hoặc Ada 12. Code gọi takeHit thông qua kiểu Character và cuối cùng in ra mọi status().
Đặt code của bạn trong Character.kt, Warrior.kt, Mage.kt và Rogue.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 party = input[0].split(",").map { spec ->
val (kind, name) = spec.split(":")
when (kind) {
"warrior" -> Warrior(name)
"mage" -> Mage(name)
else -> Rogue(name)
}
}
for (cmd in input.drop(1)) {
val (target, damage) = cmd.split(" ")
for (c in party) {
if (target == "all" || target == c.name) c.takeHit(damage.toInt())
}
}
for (c in party) println(c.status())
}
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
1Lớp và đối tượng
Làm việc với tệpĐịnh nghĩa lớpHàm khởi tạo chínhKhối init và kiểm tra tính hợp lệHàm khởi tạo phụÔn tập: thẻ thư viện4Kế thừa
Lớp mở và lớp conGhi đè thành viênsuper và hàm khởi tạoLớp trừu tượngÔn tập: Danh bạ nhân viên2Thuộc tính và tính đóng gói
Trình truy cập tùy chỉnhBộ sửa đổi phạm vi truy cậpSetter riêng tưlazy và lateinitÔn tập: tài khoản ngân hàng5Giao diện
Khai báo giao diệnPhương thức mặc địnhNhiều giao diệnGiao diện như kiểu dữ liệuÔn tập: Phương thức thanh toán3Đối tượng và đối tượng đồng hành
Khai báo đối tượngĐối tượng đồng hànhHằng số với constBiểu thức đối tượngÔn tập: Phòng bán vé6Tính đa hình
Gọi phương thức độngKiểm tra kiểu và ép kiểu thông minhwhen với kiểu dữ liệuHàm mở rộngÔn tập: Người chăm sóc sở thúTự luyện tập: Playground Kotlin