Kotlin泛型

时间:2020-02-23 14:37:30  来源:igfitidea点击:

在本教程中,我们将研究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>。