美文网首页
Kotlin---泛型

Kotlin---泛型

作者: None_Ling | 来源:发表于2019-03-06 20:00 被阅读0次

Kotlin不变型泛型

Kotlin的不变型泛型和Java一样,通过声明泛型类型来使用泛型类。而该种泛型声明后,则无法使用父类方法与属性。在编译时候,会将泛型擦除。

fun test() {
     val m = Noob<Int>()
     val n = Noob<String>()
}

class Noob<T> {
     var a: T? = null

     fun print() {
         println(a)
     }
}

泛型的单继承关系

如果使用单继承关系的话,也和Java相同,在泛型定义时,使用继承即可

fun test() {
     val m = Noob<Double>()
     val n = Noob<Float>()
}

// 指定泛型T是Number的子类
class Noob<T : Number> { 
     var a: T? = null

     fun print() {
         println(a?.toInt())
     }
}

泛型的多继承关系

当泛型需要使用多继承关系的话,则可以使用where子句来约束该泛型的每一个子类。

class Noob<T> where T : Number, T : Comparable<T> {
      lateinit var a: T

      fun compare(c: T?): Int {
          print(c?.toInt())
         return c?.compareTo(a) ?: 0
      }
}

协变(Covariant)与逆变(Contravariance)

在Java中同样也有这两种类型变换。对于Java而言,也就是:

  • 协变:List<? extends String>
  • 逆变:List<? super String>

通过继承,我们可以让A继承B,而协变可以理解成A->B,而逆变则可以理解成B->A。

在Java中,可能会出现这种情况:

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

而一般认为,String是Object的子类,而List<Object> objs = strs理论上是正确的,但是编译会出错,因为List的泛型是不变型的,也就是定义的泛型是Object就必须是Object,而不能是它的子类。

而这极大的限制了程序的灵活性。于是就产生了协变与逆变。

首先看一下协变:

class A{}
class B extends A{}

List<B> listB = new ArrayList();
List<? extends B> objList = listB; // 正常编译
objList.add(new B()); // 编译出错
B m = objList.get(0); // 正常编译

通过List<? extends B>定义了协变,允许从列表中获取的对象都可以转换成B的引用,但是不允许往该列表中添加对象。

在看一下逆变:

class A{}
class B extends A{}

List<A> listA = new ArrayList();
List<? super A> objList = listA; // 正常编译
objList.add(new B()); // 正常编译
A m = objList.get(0); // 编译出错

通过List<? super A>定义了逆变,允许向列表中添加以A为父类的B类对象,而不允许从列表中获取对象。

通过协变与逆变的方式,在保证代码灵活性的同时,也定义了代码的上下边界,保证代码的安全性。

Kotlin中的协变与逆变

前人总结过使用协变与逆变的时机,即:PECS。也就是:

  • Product Extends,Consumer Super

也就是,当你使用它来向外输出数据时,可认为它是Productor,则需要使用Extends,而当使用它来接收外部数据时,则可认为它是Consumer,则需要使用Super

从上例可以看到,协变可以从objList中成功获取B对象,说明此时objList则是作为Productor向外部输出数据,所以需要使用extends。而逆变可以允许objList中添加B对象,则可认为此时objList是作为Consumer来消费外部传入的数据。

而在Kotlin中使用outin来实现协变与逆变。

首先定义三个类,递增继承。

open class Man(var age: Int)

open class YoungMan(age: Int, var school: String) : Man(age)

class OldMan(age: Int, school: String, var company: String) : YoungMan(age, school)

Kotlin的协变,也就是Productor,使用out来限制不允许输入:

val youngManList: MutableList<out YoungMan> = ArrayList()
youngManList.add(YoungMan(10, "")) // 编译出错,限制往列表中添加元素
val x: YoungMan = youngManList.get(0) // 编译通过

而逆变,也就是Consumer,则使用in来限制不允许输出:

val youngManList: MutableList<in YoungMan> = ArrayList()
youngManList.add(YoungMan(10, "")) //编译通过
val x: YoungMan = youngManList.get(0) // 编译出错,限制从列表中获取元素

而在普通的类中使用也是同样的效果,当使用in时:

class MyClass<in T> where T : Number {

        fun printT(x: T) {  // 编译通过
            x.toLong()
        }

        fun getT(): T? {  //  编译错误,
            return null
        }
    }

当使用out时:

class MyClass<out T> where T : Number {

        fun printT(x: T) {  // 编译错误
            x.toLong()
        }

        fun getT(): T? {   // 编译通过
            return null
        }
    }

相关文章

  • Kotlin---泛型

    Kotlin不变型泛型 Kotlin的不变型泛型和Java一样,通过声明泛型类型来使用泛型类。而该种泛型声明后,则...

  • 泛型 & 注解 & Log4J日志组件

    掌握的知识 : 基本用法、泛型擦除、泛型类/泛型方法/泛型接口、泛型关键字、反射泛型(案例) 泛型 概述 : 泛型...

  • 【泛型】通配符与嵌套

    上一篇 【泛型】泛型的作用与定义 1 泛型分类 泛型可以分成泛型类、泛型方法和泛型接口 1.1 泛型类 一个泛型类...

  • 泛型的使用

    泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法 泛型类 泛型接口 泛型通配符 泛型方法 静态方法与...

  • Java 泛型

    泛型类 例如 泛型接口 例如 泛型通配符 泛型方法 类中的泛型方法 泛型方法与可变参数 静态方法与泛型 泛型上下边...

  • 探秘 Java 中的泛型(Generic)

    本文包括:JDK5之前集合对象使用问题泛型的出现泛型应用泛型典型应用自定义泛型——泛型方法自定义泛型——泛型类泛型...

  • Web笔记-基础加强

    泛型高级应用 自定义泛型方法 自定义泛型类 泛型通配符? 泛型的上下限 泛型的定义者和泛型的使用者 泛型的定义者:...

  • 重走安卓进阶路——泛型

    ps.原来的标题 为什么我们需要泛型? 泛型类、泛型接口和泛型方法(泛型类和泛型接口的定义与泛型方法辨析); 如何...

  • Kotlin泛型的高级特性(六)

    泛型的高级特性1、泛型实化2、泛型协变3、泛型逆变 泛型实化 在Java中(JDK1.5之后),泛型功能是通过泛型...

  • Java 19-5.1泛型

    泛型类定义泛型类可以规定传入对象 泛型类 和泛型方法 泛型接口 如果实现类也无法确定泛型 可以在继承类中确定泛型:

网友评论

      本文标题:Kotlin---泛型

      本文链接:https://www.haomeiwen.com/subject/syhtpqtx.html