Lớp tổng quát
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 38 trên 57.
Một lớp cũng có thể có tham số kiểu. Box<T> chứa một giá trị thuộc bất kỳ kiểu nào, và mỗi đối tượng xác định kiểu đó: Box<Int> chỉ chấp nhận các giá trị Int:
class Box<T>(var value: T) {
fun describe() = "box with $value"
}Bên trong main:
val numberBox = Box(42)
val textBox = Box("hello")
numberBox.value += 1
println(numberBox.describe())
println(textBox.describe())
// numberBox.value = "x" // lỗi: String không phải là IntĐầu ra:
box with 43
box with helloContainer là các lớp tổng quát phổ biến. Một cách triển khai Stack<T> có thể dùng cho mọi kiểu phần tử, và trình biên dịch sẽ kiểm tra dữ liệu đưa vào cũng như dữ liệu nhận ra:
class Stack<T> {
private val items = mutableListOf<T>()
val size: Int
get() = items.size
fun push(item: T) { items.add(item) }
fun pop(): T? = items.removeLastOrNull()
fun peek(): T? = items.lastOrNull()
}Bên trong main:
val stack = Stack<String>()
stack.push("a")
stack.push("b")
println(stack.pop())
println(stack.peek())
println(stack.size)Đầu ra:
b
a
1Trình biên dịch suy luận kiểu của tham số từ các đối số của hàm khởi tạo nếu có, như trong Box(42). Nếu không, hãy viết kiểu đó: Stack<String>(). Các đối tượng có tham số kiểu khác nhau thuộc các kiểu khác nhau, vì vậy Stack<String> không bao giờ bị nhầm lẫn với Stack<Int>. Các hàm của lớp tổng quát cũng có thể thêm tham số kiểu riêng:
class Box<T>(val value: T) {
fun <R> map(f: (T) -> R): Box<R> = Box(f(value))
}Bên trong main:
val length = Box("kotlin").map { it.length }
println(length.value + 1)Đầu ra:
7Các giao diện tổng quát tuân theo cùng quy tắc. Một lớp có thể triển khai giao diện tổng quát hoặc triển khai giao diện cho một đối số kiểu cụ thể:
interface Repository<T> {
fun add(item: T)
fun all(): List<T>
}
class MemoryRepository<T> : Repository<T> {
private val items = mutableListOf<T>()
override fun add(item: T) { items.add(item) }
override fun all() = items.toList()
}
class TagRepository : Repository<String> {
private val tags = sortedSetOf<String>()
override fun add(item: String) { tags.add(item.lowercase()) }
override fun all() = tags.toList()
}Bên trong main:
val ids = MemoryRepository<Int>()
ids.add(3); ids.add(1)
val tags = TagRepository()
tags.add("Kotlin"); tags.add("JVM")
println("${ids.all()} ${tags.all()}")Đầu ra:
[3, 1] [jvm, kotlin]Thử thách
DễViết lớp tổng quát History<T>(limit) để lưu lại các phần tử gần nhất, tối đa là limit phần tử. add(item) thêm một phần tử vào cuối và xóa phần tử cũ nhất khi có nhiều hơn limit phần tử. latest là phần tử mới nhất hoặc null, còn all() trả về các phần tử từ cũ nhất đến mới nhất dưới dạng danh sách chỉ đọc. Sau đó, viết phần mở rộng History<Int>.average(), chỉ có ở các lịch sử chứa số và trả về giá trị trung bình dạng số nguyên của các số được giữ lại (trả về 0 nếu danh sách trống).
Code được cung cấp đọc giới hạn, một dòng từ và một dòng số. Code này điền dữ liệu vào History<String> và History<Int>, rồi in ra các từ được giữ lại, từ mới nhất, các số được giữ lại, giá trị trung bình của chúng và empty latest: null.
Viết code của bạn trong History.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 (input) và đầu ra (output) đã được cung cấp: giữ nguyên
val input = generateSequence(::readLine).toList()
val limit = input[0].toInt()
val words = History<String>(limit)
for (w in input[1].split(" ")) words.add(w)
val numbers = History<Int>(limit)
for (n in input[2].split(" ")) numbers.add(n.toInt())
println("words: ${words.all()}")
println("latest word: ${words.latest}")
println("numbers: ${numbers.all()}")
println("average: ${numbers.average()}")
println("empty latest: ${History<Char>(3).latest}")
}
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án8Kiểu tổng quát
Hàm tổng quátLớp tổng quátRàng buộc kiểuTính biến thiên với in và outÔn tập: Hàng đợi ưu tiênTự luyện tập: Playground Kotlin