泛型

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 泛型,你了解类型擦除吗?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。