Kotlin 泛型是一种强大的编程工具,用于创建可以操作多种数据类型的类、接口和方法。通过使用泛型,你可以避免类型转换的麻烦,并使代码更加简洁和安全。
泛型的基本概念
泛型允许你在定义类或函数时指定一个或多个类型参数。这些类型参数可以在类或函数内部使用,以确保类型安全并提高代码的可重用性。
定义泛型类
你可以通过在类名后的尖括号<>中声明类型参数来定义泛型类。例如:
class Box<T>(t: T) {
var value = t
}在这个例子中,T是一个类型参数,可以是任何类型。Box类接受一个类型为T的参数,并将其存储在一个名为value的变量中。
使用泛型类
定义了泛型类之后,你可以使用具体的类型实例化这个类。例如:
val intBox = Box<Int>(10)
val stringBox = Box<String>("Hello, Kotlin")在上述示例中,intBox是一个存储整数的Box对象,而stringBox是一个存储字符串的Box对象。
泛型函数
除了类之外,你还可以定义泛型函数。泛型函数允许你在函数签名中声明类型参数,并在函数体中使用它们。
定义泛型函数
你可以通过在函数名后的尖括号<>中声明类型参数来定义泛型函数。例如:
fun <T> copyData(data: List<T>): List<T> {
return data.toMutableList()
}在这个例子中,T是一个类型参数,可以是任何类型。copyData函数接受一个类型为T的列表,并返回一个新的列表。
使用泛型函数
定义了泛型函数之后,你可以使用具体的类型调用这个函数。例如:
val numbers = listOf(1, 2, 3)
val copiedNumbers = copyData(numbers)
val strings = listOf("one", "two", "three")
val copiedStrings = copyData(strings)在上述示例中,copiedNumbers是一个与numbers相同类型的列表,而copiedStrings是一个与strings相同类型的列表。
泛型约束
有时你需要限制泛型类型参数只能是特定类型或实现特定接口的类型。你可以通过使用where关键字来实现这一点。
定义泛型约束
你可以通过在类型参数后面添加where关键字和约束条件来定义泛型约束。例如:
fun <T> compare(a: T, b: T): Boolean where T : Comparable<T> {
return a == b
}在这个例子中,T必须实现Comparable<T>接口才能使用compare函数。
使用泛型约束
定义了泛型约束之后,你可以使用符合约束条件的具体类型调用这个函数。例如:
val result1 = compare(1, 2)
val result2 = compare("hello", "world")在上述示例中,result1比较两个整数,而result2比较两个字符串。
多个类型参数
你也可以为一个类或函数定义多个类型参数,这在处理需要多个不同类型的场景时非常有用。
定义多个类型参数
你可以通过在尖括号<>中列出所有类型参数来定义多个类型参数。例如:
class Pair<F, S>(first: F, second: S) {
val firstValue = first
val secondValue = second
}在这个例子中,F和S是两个独立的类型参数。
使用多个类型参数
定义了多个类型参数之后,你可以使用具体的类型实例化这个类。例如:
val pair = Pair<Int, String>(10, "Hello, Kotlin")
在上述示例中,pair是一个包含整数和字符串的Pair对象。
高级特性
除了基本概念外,Kotlin还提供了许多高级的泛型特性,如协变和逆变、类型擦除等。这些特性使得泛型在Kotlin中更加灵活和强大。
协变和逆变
协变和逆变允许你在某些情况下放宽或收紧类型参数的类型。通过使用out关键字可以声明协变类型参数,而使用in关键字可以声明逆变类型参数。
协变
协变允许你将子类型视为父类型。例如:
interface ReadOnlyList<out T> {
fun get(index: Int): T
}
val readOnlyNumbers: ReadOnlyList<Int> = listOf(1, 2, 3)
val readOnlyAny: ReadOnlyList<Any> = readOnlyNumbers在这个例子中,ReadOnlyList接口被声明为协变的,因此readOnlyNumbers可以被视为ReadOnlyList<Any>。
逆变
逆变允许你将父类型视为子类型。例如:
// www.javascriptcn.com code example
interface Writer<in T> {
fun write(value: T)
}
fun print(writer: Writer<Any>) {
writer.write(10)
writer.write("Hello, Kotlin")
}
print(object : Writer<Any> {
override fun write(value: Any) {
println(value)
}
})在这个例子中,Writer接口被声明为逆变的,因此Writer<Any>可以被视为Writer<Int>或Writer<String>。
类型擦除
虽然Kotlin保留了运行时的类型信息,但在编译时,泛型类型参数会被替换为它们的上限(通常是Any?)。这种机制称为类型擦除,它使得泛型代码在运行时更高效。
泛型中的空安全
Kotlin的空安全特性使得在使用泛型时可以更好地控制类型安全。例如,你可以声明一个只接受非空类型参数的泛型类:
class NonNullBox<T : Any>(t: T) {
var value = t
}在这个例子中,NonNullBox类只接受非空类型参数。
通过掌握这些基本概念和高级特性,你可以充分利用Kotlin泛型的强大功能,编写出更简洁、更安全和更具可重用性的代码。