Kotlin-泛型基础知识
概念:泛型是一种类型参数化的机制,允许我们编写具有通用性的代码。
Kotlin 中的泛型使用尖括号 < > 来定义,可以用在类,接口,方法上。
1. 声明泛型类型
在 Kotlin 中,可以在类、接口、函数等处声明泛型。泛型类型使用尖括号 < > 来声明,例如:
class Box<T>(var value: T)
2. 使用泛型
使用泛型类时,你需要指定具体的类型:
val intBox = Box<Int>(10)
val stringBox = Box("Hello")
3. 泛型函数
你也可以为函数声明泛型:
fun <T> printElement(element: T) {
println(element)
}
4. 泛型约束
有时你可能想要限制泛型的类型,例如,只允许传入数字类型。在 Kotlin 中,你可以使用 : Type 来约束类型参数:
fun <T : Number> printNumber(number: T) {
println(number)
}
5、协变(Covariance)表示输出、生产、获取,使用 out 关键字。
协变允许你将一个类型视为另一个类型的子类型。换句话说,如果一个类型A是类型B的子类型,那么类型A的协变泛型可以安全地被视为类型B的泛型。在Kotlin中,协变通过在泛型声明中使用out关键字来实现。
Kotlin 中的 <out T> 类似于 Java 中的 <? extends T>。
6、逆变(Contravariance)表示输入、消费、传递,使用 in 关键字
逆变与协变相反,它允许你将一个类型视为另一个类型的超类型。在Kotlin中,逆变通过在泛型声明中使用in关键字来实现。
Kotlin 中的 <in T> 类似于 Java 中的 <? super T>。
Kotlin Java 边界
协变(out) List<out TextView> List<? extends TextView> 上限
逆变(in) List<in TextView> List<? super TextView> 下限
7. 使用星号投影(Wildcard projections)处理不确定的泛型类型
有时你可能需要处理不确定的泛型类型,可以使用星号投影(例如 List<*>):
fun processList(list: List<*>) { // 处理任何类型的列表
// 注意:不能安全地访问元素,因为类型未知
}
例子:
class TestGeneric3 { @Test fun testGeneric3(){ //基础泛型 //test1() //逆变 //test2() //协变 //test3() //通配符* //test4() //通配符 test5() } /** * 基础泛型 */ private fun test1(){ var box1 = Box1(1) var box11 = Box1("abc") println("test1 : ${box1.getItem()} , ${box11.getItem()}") var box2 = Box2("kotlin", 8) println("test2 : ${box2.getParam1()}, ${box2.getParam2()}") println("convertToInt : " + convertToInt(33.83)) } /** * 在使用泛型时,可以限制类型参数的上界。这可以通过使用冒号 : 加上类型约束来实现。例如,我们可以指定一个类型参数必须是某个类的子类,或者实现了某个接口。下面是一个使用类型约束的例子: */ private fun <T : Number> convertToInt(value : T) : Int{ return value.toInt() } /** * 测试in:逆变 */ private fun test2(){ var list = ArrayList<MyComparable<in String>>() var myComparable = object : MyComparable<String>{ override fun myCompare(param: String): Int { return param.toInt() } } list.add(myComparable) var myComparable2 = object : MyComparable<String>{ override fun myCompare(param: String): Int { return param.toInt() } } list.add(myComparable2) test2Print(list) } /** * in:逆变 */ private fun test2Print(list : List<MyComparable<in String>>){ list.forEachIndexed { index, myComparable -> println("in逆变:index : ${index} , value : ${myComparable.myCompare("123${index}")}") } } private fun test3(){ var myOut1 = testMyOut1() var myOut2 = testMyOut2() println("测试协变:${myOut1.myOut()} , ${myOut2.myOut()}") } private fun testMyOut1() : MyOut<out String>{ var myOut1 = object : MyOut<String>{ override fun myOut(): String { return "协变1" } } return myOut1 } private fun testMyOut2() : MyOut<out Int>{ var myOut2 = object : MyOut<Int>{ override fun myOut(): Int { return 666 } } return myOut2 } private fun test4(){ var list = ArrayList<Int>() list.add(2) list.add(8) list.add(3) var list2 = ArrayList<String>() list2.add("test66") list2.add("test88") test4Print(list) test4Print(list2) } /** * 通配符* */ private fun test4Print(list : ArrayList<*>){ list.forEachIndexed { index, any -> println("test4 通配符:${index} , ${any}") } } private fun test5(){ var list = ArrayList<Int>() list.add(3) list.add(5) list.add(8) test5Print(list) } /** * 通配符 - 只能是Number类型, 不能是字符串 * 使用关键字 out 来支持协变,等同于 Java 中的上界通配符 ? extends。 * 使用关键字 in 来支持逆变,等同于 Java 中的下界通配符 ? super。 */ private fun test5Print(list : ArrayList<out Number>){ list.forEachIndexed { index, number -> println("通配符:${index}, ${number}") } } } //<T> 表示泛型类型参数,并且 T 是一个类型参数,编译器会根据实参的类型推断出 T 的具体类型 class Box1<T>(private val item : T){ //泛型函数返回类型为T fun getItem() : T{ return item } } //支持多个类型参数的泛型定义 class Box2<T, R>(private val param1 : T, private val param2 : R){ fun getParam1() : T{ return param1 } fun getParam2() : R{ return param2 } } //逆变:使用 in 修饰符声明的类型参数只能用作输入(即参数类型),不能用作输出(即返回类型)。它允许我们使用指定的类型参数的超类作为泛型类型的实参 interface MyComparable<in T> { fun myCompare(param : T) : Int } //协变:使用 out 修饰符声明的类型参数只能用作输出(即返回类型),不能用作输入(即参数类型)。它允许我们使用指定的类型参数的子类作为泛型类型的实参。在函数中,协变类型参数只能作为方法的返回类型。 interface MyOut<out T>{ fun myOut() : T }