Menu
CoddyTech

Hàm 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 37 trên 57.

Một hàm tổng quát hoạt động với mọi kiểu dữ liệu. Tham số kiểu, được viết trong dấu ngoặc nhọn trước tên hàm, đại diện cho kiểu mà bên gọi hàm sử dụng. T là tên thường dùng:

fun <T> firstOr(list: List<T>, default: T): T = if (list.isEmpty()) default else list[0]

Bên trong main:

println(firstOr(listOf(3, 5), 0) + 1)
println(firstOr(listOf<String>(), "none").uppercase())

Đầu ra:

4
NONE

Kotlin suy luận tham số kiểu từ các đối số. Khi không có gì để suy luận kiểu từ đó, bạn tự viết tham số kiểu, như listOf<String>() ở trên:

fun <T> repeatItem(item: T, times: Int): List<T> = List(times) { item }

Bên trong main:

println(repeatItem("ab", 3))
println(repeatItem(7, 2))
val empty = repeatItem<Double>(1.5, 0)
println(empty)

Đầu ra:

[ab, ab, ab]
[7, 7]
[]

Một hàm có thể có nhiều tham số kiểu, và tham số có kiểu hàm có thể sử dụng chúng. Đây là cách các hàm thư viện như map hoạt động với mọi kiểu phần tử và kiểu kết quả:

fun <A, B> transform(items: List<A>, f: (A) -> B): List<B> {
    val out = mutableListOf<B>()
    for (item in items) out.add(f(item))
    return out
}

fun <K, V> invert(map: Map<K, V>): Map<V, K> = map.entries.associate { (k, v) -> v to k }

Bên trong main:

println(transform(listOf("kiwi", "fig")) { it.length })
println(invert(mapOf("a" to 1, "b" to 2)))

Đầu ra:

[4, 3]
{1=a, 2=b}

Một tham số kiểu giữ nguyên kiểu của nơi gọi. firstOr(listOf(3, 5), 0) trả về một Int, nên + 1 biên dịch được. Một phiên bản viết cho List<Any> cũng chấp nhận mọi danh sách, nhưng sẽ trả về Any và buộc bạn ép kiểu ở mỗi lần gọi:

fun firstAny(list: List<Any>, default: Any): Any = if (list.isEmpty()) default else list[0]
// firstAny(listOf(3, 5), 0) + 1               // lỗi: Any không có plus
(firstAny(listOf(3, 5), 0) as Int) + 1         // Biên dịch được, nhưng cần ép kiểu
firstOr(listOf(3, 5), 0) + 1                   // Kiểu tổng quát: kết quả là một Int
challenge icon

Thử thách

Dễ

Viết hai hàm tổng quát hoạt động với danh sách thuộc mọi kiểu dữ liệu. countWhere(items, predicate) trả về số phần tử thỏa mãn điều kiện, là một hàm (T) -> Boolean. mostCommon(items) trả về phần tử xuất hiện nhiều nhất (nếu số lần xuất hiện bằng nhau, chọn phần tử xuất hiện trước trong danh sách), hoặc null nếu danh sách trống.

Code được cung cấp đọc một dòng gồm các từ và một dòng gồm các số, rồi in ra số từ dài hơn 3 ký tự, từ phổ biến nhất, số lượng số chẵn, số phổ biến nhất và nothing: null cho danh sách trống.

Đặt code của bạn trong ListTools.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 đã cho: giữ nguyên
    val input = generateSequence(::readLine).toList()
    val words = input[0].split(" ")
    val numbers = input[1].split(" ").map { it.toInt() }
    println("long words: ${countWhere(words) { it.length > 3 }}")
    println("most common word: ${mostCommon(words)}")
    println("even numbers: ${countWhere(numbers) { it % 2 == 0 }}")
    println("most common number: ${mostCommon(numbers)}")
    println("nothing: ${mostCommon(listOf<String>())}")
}
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