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>
。
逆变
逆变允许你将父类型视为子类型。例如:
-- -------------------- ---- ------- --------- --------- -- - --- ------------ -- - --- ------------- ------------ - ---------------- -------------------- -------- - ------------ - ----------- - -------- --- ------------ ---- - -------------- - --
在这个例子中,Writer
接口被声明为逆变的,因此Writer<Any>
可以被视为Writer<Int>
或Writer<String>
。
类型擦除
虽然Kotlin保留了运行时的类型信息,但在编译时,泛型类型参数会被替换为它们的上限(通常是Any?
)。这种机制称为类型擦除,它使得泛型代码在运行时更高效。
泛型中的空安全
Kotlin的空安全特性使得在使用泛型时可以更好地控制类型安全。例如,你可以声明一个只接受非空类型参数的泛型类:
class NonNullBox<T : Any>(t: T) { var value = t }
在这个例子中,NonNullBox
类只接受非空类型参数。
通过掌握这些基本概念和高级特性,你可以充分利用Kotlin泛型的强大功能,编写出更简洁、更安全和更具可重用性的代码。