我的Kotlin学习之旅(七)

1.泛型:
Kotlin中也有泛型,和Java类似
例如:

 class Person <T>(name: T){
    var personName = name
}

这个类的主构造函数,初始化时需要指定泛型,
下面是测试:

val person = Person<String>("ckw")

在这里我们指定了主构造函数中的参数的类型是String,

person.personName

这里的值就是”ckw”

但是如果类型参数可以推断出来,例如从构造函数的参数或者从其他途径,允许省略类型参数
例如我们可以这样写:

val person = Person("ckw")

由于可以推断出参数的类型是String类型的,所以T的类型可以省略。

2.型变
Java 类型系统中最棘手的部分之一是通配符类型,通配符类型参数 ? extends E
表示此方法接受 E 或者 E 的 一些子类型对象的集合,而不只是 E 自身
Java 中的泛型是不型变的,这意味着 List<String> 并不是List<Object> 的子类型。

为什么这样? 如果 List 是型变的,如下代码会通过编译然后导致运行时异常:

List<String> strs = new ArrayList<String>();
List<Object> objs = strs; // !!!即将来临的问题的原因就在这里。Java 禁止这样!
objs.add(1); // 这里我们把一个整数放入一个字符串列表
String s = strs.get(0); // !!! ClassCastException:无法将整数转换为字符串

哎,关于型变,我觉得文档上翻译地有一点多,Java部分的我就不写了,直接看Kotlin部分。简而言之,Kotlin对于型变这块,对其进行了升级。
直接来看Kotlin的概念:声明处型变
首先看一下它是为了解决什么问题而提出来的:
假设有一个泛型接口 Source,该接口中不存在任何以 T 作为参数的方法,只是方法返回 T 类型值:

interface Source<T> {
  T outT();
}

那么,在 Source 类型的变量中存储 Source 实例的引用是极为安全的。但是 Java 并不知道这一点,并且仍然禁止这样操作:

void demo(Source<String> strs) {
  Source<Object> objects = strs; // !!!在 Java 中不允许
}

为此,Kotlin中提供了两个修饰符:inout
下面来看一下out
解释:当一个类 C 的类型参数 T 被声明为 out 时,它就只能出现在 C 的成员的输出位置,但回报是 C 可以安全地作为 C的超类。

简而言之,他们说类 C 是在参数 T 上是协变的,或者说 T 是一个协变的类型参数。 你可以认为 C 是 T 的生产者,而不是 T 的消费者。
例子:

interface Source<out T> {
    fun nextT(): T
}

fun demo(strs: Source<String>) {
    val objects: Source<Any> = strs // 这个没问题,因为 T 是一个 out-参数
    // ……
}

in
使得一个类型参数逆变:只可以被消费而不可以被生产。逆变类型的一个很好的例子是 Comparable:

interface Comparable<in T> {
    operator fun compareTo(other: T): Int
}

fun demo(x: Comparable<Number>) {
    x.compareTo(1.0) // 1.0 拥有类型 Double,它是 Number 的子类型
    // 因此,我们可以将 x 赋给类型为 Comparable <Double> 的变量
    val y: Comparable<Double> = x // OK!
}

我个人的理解是标记为out的类型只能用作输出,例如一个函数的返回值,in的类型只能作为函数的输入,例如函数的参数,这一块个人理解的还不够透彻,一知半解吧,需要以后在实践中应用加深理解!

3.泛型函数:
不仅类可以有类型参数。函数也可以有。类型参数要放在函数名称之前:

fun <T> singletonList(item: T): List<T> {
    // ……
}

fun <T> T.basicToString() : String {  // 扩展函数
    // ……
}

要调用泛型函数,在调用处函数名之后指定类型参数即可:

val l = singletonList<Int>(1)

4.泛型约束
Kotlin中的冒号
最常见的约束类型是与 Java 的 extends 关键字对应的 上界

默认的上界(如果没有声明)是 Any?。在尖括号中只能指定一个上界。 如果同一类型参数需要多个上界,我们需要一个单独的 where-子句:

fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
    where T : CharSequence,
          T : Comparable<T> {
    return list.filter { it > threshold }.map { it.toString() }
}

哎,学习之路任重道远,这块内容还是要多学习学习

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章