Kotlin泛型
在本教程中,我们将研究Kotlin的泛型和变异性。
泛型
泛型是一项强大的功能,它使我们能够定义可以使用不同类型进行操作的通用类/方法/属性,同时还要检查编译时类型的安全性。
它们通常用于集合中。
泛型类型是通过类型进行参数化的类或者接口。
我们使用尖括号(<>)来指定type参数。
要了解泛型,我们需要了解类型。
每个类的类型通常与该类相同。
但是,在Kotlin中,可否为null的类型(例如String)?将不会被视为程序类型。
List <String>等也是如此。
以下是Kotlin泛型类的示例:
fun main(args: Array<String>) { var a = A("") var b: A<Int> = A(1) //explicit } class A<T>(argument: T) { var result = argument }
我们使用显式类型实例化了该类,并允许编译器进行推断。
方差
方差就是用一个子类型或者超类型替换一个类型。
以下内容在Java中起作用:
Number[] n = newNumber[2]; n[0] = newInteger(1); n[1] = newDouble(47.24);
这意味着Java中的数组是协变的。
变量均值,替换为:子类型是可接受的。
超类型不是。
因此,使用协变原理,以下Java代码也可以正常工作:
Integer[] integerArray = {1,2,3}; Number[] numberArray = integerArray;
Number类是Integer类的父级,因此继承和子类型化原则适用于上面。
但是,如果我们做这样的事情,上面的分配是有风险的:
numberArray[1] = 4.56 //compiles successfully
因为我们不能将Double存储为Int,这将导致Java中的运行时异常。
Kotlin数组通过默认使数组不变而背离了这一原理。
不变表示,代入:不允许子类型。
不允许超类型。
因此上述Kotlin阵列不会发生运行时错误。
val i = arrayOf(1, 2, 3) val j: Array<Any> = i //this won't compile.
因此,Kotlin和Java之间的主要差异之一是:
Kotlin数组是不变的。
Java数组是协变的。
当将上述概念与Java和Kotlin的泛型和集合一起使用时,请不要编译。
import java.util.ArrayList; import java.util.List; public class Hi { public static void main(String[] args) { List<String> stringList= new ArrayList<>(); List<Object> myObjects = stringList; //compiler error } }
默认情况下,泛型类型是不变的。
在Java中,我们使用通配符来使用不同类型的方差。
除了不变以外,还有两种主要的方差类型。
- 协变
- 逆变的
协变
一个? extend Object
是一个通配符参数,它使类型成为协变。
我们之前的Java代码现在可以正常工作了。
public class Hi<T> { public static void main(String[] args) { Hi<Integer> j = new Hi(); Hi<Object> n = j; //compiler error Hi<? extends Object> k = j; //this works fine } }
第三条语句是使用通配符参数的协方差示例。
修改器out用于在Kotlin中应用协方差。
fun main(args: Array<String>) { val x: A<Any> = A<Int>() //Error: Type mismatch val y: A<out Any> = A<String>() //Works val z: A<out String> = A<Any>() //Error } class A<T>
在Kotlin中,我们可以在类的参数类型上直接注释通配符参数。
这称为声明站点差异。
fun main(args: Array<String>) { val x: A<Any> = A<Int>() } class A<out T>
上面的Kotlin代码看起来比Java代码更具可读性。
fun main(args: Array<String>) { var correct: Container<Vehicle> = Container<Car>() var wrong: Container<Car> = Container<Vehicle>() } open class Vehicle class Car : Vehicle() class Container<out T>
总结Kotlin中的协方差:用完用于设置替代子类型。
并非相反
逆变
这与协方差正好相反。
它用于替换子类型中的超类型值。
反之亦然。
它使用in
修饰符
fun main(args: Array<String>) { var correct: Container<Car> = Container<Vehicle>() var wrong: Container<Vehicle> = Container<Car>() } open class Vehicle class Car : Vehicle() class Container<in T>
Kotlinin
等于 Java的<? super T>。
Kotlinout
等于Java的<? extends T>。