Kotlin 泛型详解
声明一个泛型类
class Box<T>(t: T) {
var value = t
}
声明一个泛型方法
fun <T> requestT(t: T) {
}
泛型约束
List<Int> 和 List<Number> 是没有关系的。
在Java中可以用泛型约束定义这种关系。
fun <T : Comparable<T>> sort(list: List<T>) {
// ……
}
对于多个上界约束条件,可以用 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() }
}
型变
Kotlin 中没有通配符类型,它有两个其他的东西:声明处型变(declaration-site variance)与类型投影(type projections)。
声明处的类型变异使用协变注解修饰符:in、out,消费者 in, 生产者 out。
- in 消费者(接受T 作为参数)
// 定义一个支持逆变的类
class Runoob<in A>(a: A) {
fun foo(a: A) {
}
}
fun main(args: Array<String>) {
var strDCo = Runoob("a")
var anyDCo = Runoob<Any>("b")
strDCo = anyDCo
}
out 生产者(接受T 作为返回值)
// 定义一个支持协变的类
class Runoob<out A>(val a: A) {
fun foo(): A {
return a
}
}
public fun addAll(elements: Collection<E>): Boolean
而Collection 是型变的
public interface Collection<out E> : Iterable<E> {
- 对应的是Java中的
boolean addAll(Collection<? extends E> c);
通配符
在Java 中,当我们不知道泛型具体类型的时候可以用 ?来代替具体的类型来使用,比如下面的写法:
Class<?> cls = numbers.getClass();
* 在哪些场合下可以或者不可以使用呢?
fun <T> hello(args: Array<T>){
...
}
...
hello<*>(args) // ERROR!!
* 不允许作为函数和变量的类型的泛型参数!
interface Foo<T>
class Bar : Foo<*> // ERROR!
* 不能直接作为父类的泛型参数传入!
class Bar : Foo<Foo<*>>
这是正确的。注意,尽管 * 不能直接作为类的泛型参数,Foo<*> 却可以,按照前面官方给出的说法,它在读时等价于Foo<out Any> 写时等价于 Foo<in Nothing>
fun hello(args: Array<*>){
...
}
同样,这表示接受的参数的类型在读写时分别等价于Array<out Any> 和 Array<in Nothing>
原生类型
Java中原生类型是为了向后兼容
List list = new ArrayList();
在kotlin这种代码是不被编译器允许的
val list = ArrayList() //Error
val list = ArrayList<Any?>() //right
但上面 ArrayList<Any?>()不是协变的
var list = ArrayList<Any?>()
val integers = ArrayList<Int>()
list = integers // ERROR!
类型擦除
从运行时的角度看Java与kotlin是类型擦除的。
考虑下面的代码:
List<String> l1 = new ArrayList<>();
List<Integer> l2 = new ArrayList<>();
System.out.print("l1 class:" + l1.getClass()+ "\n");//print class:class java.util.ArrayList
System.out.print("l2 class:" + l2.getClass() + "\n"); //print class:class java.util.ArrayList
assertEquals(l1.getClass(), l2.getClass());
最终结果测试通过,输出都为: class:class java.util.ArrayList
在泛型类被类型擦除的时候,之前泛型类中的类型参数部分如果没有指定上限,如 <T> 则会被转译成普通的 Object 类型,如果指定了上限如 <T extends String> 则类型参数就被替换成类型上限。
Reference:
菜鸟教程-泛型
Kotlin 泛型详解
Java 泛型,你了解类型擦除吗?
网友评论