12.Kotlin泛型与协变及逆变原理剖析

2017-12-24  本文已影响0人  leofight

1.密封类

密封类(sealed class)
①密封类用来表示受限的类继承结构,对密封类中的某个值来说,它所属类型只能是受限的类型之一,不能是其他类型。
②在某种意义上,他们是枚举类的扩展:枚举类型的值集合也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例。
③要声明一个密封类,需要在类名前面添加 sealed 修饰符。虽然密封类也可以有子类,但是所有子类都必须在与密封类自身相同的文件中声明。(在 Kotlin 1.1 之前,该规则更加严格:子类必须嵌套在密封类声明的内部)。
④密封类本身是一个抽象的类,因此它是不能被直接实例化的,而密封类本身里面可以包含一些抽象的成员的,同时密封类不允许提供
非私有的构造方法。换句话说,密封类构造方法默认情况下是私有的。

使用密封类的关键好处在于使用 when 表达式 的时候,如果能够验证语句覆盖了所有情况,就不需要为该语句再添加一个 else 子句了。

sealed class Calculator

class Add : Calculator()

class Subtract : Calculator()

class Multiply : Calculator()

fun calculate(a: Int, b: Int, cal: Calculator) = when (cal) {
    is Add -> a + b
    is Subtract -> a - b
    is Multiply -> a * b
}

fun main(args: Array<String>) {
    println(calculate(1, 2, Add()))
    println(calculate(1, 2, Subtract()))
    println(calculate(1, 2, Multiply()))
}

3
-1
2

2.Kotlin泛型

泛型(generics),表示变量类型的参数化

class MyGenerics<T>(t: T){
    var variable: T

    init {
        this.variable = t
    }
}

fun main(args: Array<String>) {
    //var myGeneric:MyGenerics<String> = MyGenerics<String>("helloworld")//完整写法
    var myGeneric = MyGenerics("helloworld")//借助于kotlin的类型推断
    println(myGeneric.variable)
}
helloworld

3.协变(convariant)与逆变(controvariant)

   //例如List<Object>与 List<String>

    List<String> list = new ArrayList();
    List<Object> list2 = list;//编译失败,List<String> 并不是 List<Object> 的子类型。

    list2.add(new Date())

    String str = list.get(0)//ClassCastException:类型转换错误

    List<? extends Object> list ... //可以放Object类型及子类型


  //例如Collection 接口中的 addAll()方法
    //直观理解
    interface Collection<E>{
        void addAll(Collection<E> items)
    }


    void copyAll(Collection<Object to,Collection<String> from){
        to.addAll(from);//编译失败,Collection<String> 不是 Collection<Object> 的子类型
    }

  //实际情况
    interface Collection<E>{
        void addAll(Collection<? extends E> items;
    }

    Collection<String>就是Collection<? extends Obejct>的子类型 //协变

    List<? super String> //逆变

    我们如果只从中读取数据,而不往里面写入内容,那么这样的对象叫做生产者(协变);如果只向里面写入数据,而不从中读取数据,那么这样的数据叫做消费者。(逆变)

    生产者使用 ? extends E; 消费者使用? super E
  

class MyClass<out T,in M>(t: T,m: M){
    private var t: T
    private var m: M

    init {
        this.t = t
        this.m = m
    }

    fun get(): T = this.t

    fun set(m: M){
        this.m = m
    }

}

fun myTest(myClass: MyClass<String,Number>){
    var myObject: MyClass<Any,Int> = myClass
    println(myObject.get())

}

fun main(args: Array<String>) {
    var myClass = MyClass<String,Number>("abc",2);
    myTest(myClass)


}
abc

kotlin中针对协变与逆变提供了两个关键字out与in。

上一篇 下一篇

猜你喜欢

热点阅读