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中提供了两个修饰符:in 和 out
下面来看一下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() }
}
哎,学习之路任重道远,这块内容还是要多学习学习