第九章:泛型

本文详细介绍了Kotlin中的泛型类型参数及其约束,包括上界约束、类型实参的使用,以及如何在运行时处理泛型。此外,还探讨了协变、逆变和点变型的概念,解释了它们在类型参数上的应用,以及如何在泛型类和函数中正确使用这些特性。

泛型类型参数

  1. 类型参数约束:限制作为(泛型)类和(泛型)函数的类型实参的类型
    在这里插入图片描述
    如果把一个类型指定为泛型类型形参的上界约束,在泛型具体的初始化中,其对应的类型实参就必须是这个具体类型或者是它的子类型。
  2. 指定多个约束:使用关键字where
    在这里插入图片描述
  3. 没有指定上界的类型形参将会使用Any?这个默认的上界 , 如果想让类型参数始终是非空类型,需要使用Any或其子类型。

运行时的泛型

  1. JVM 上的泛型一般是通过类型擦除实现的,也就是说泛型类实例的类型实参在运行时是不保留的。Kotlin和Java一样。例如:如果你创建了一个List 并将一堆字符串放到其中,在运行时你只能看到它是一个list,不能识别中列表本打算包含的是哪种类型的元素。
    在这里插入图片描述
    在执行的时候看起来完全一样。你不能判断一个列表是包含字符串的列表还是包含其他对象的列表,也就是说is检查中不可能使用类型实参种的类型 ,运行时它的类型实参被擦除了。
    在这里插入图片描述
    需要使用:List<*>
    注意:在as和as?转换中仍然可以使用一般的泛型类型
    在这里插入图片描述
    在这里插入图片描述
  2. 内联函数的类型形参能够被实化,意味着你可以运行时引用实际的类型参数
    使用inline来修饰函数,并把类型参数使用reified(具体化)标记

    在这里插入图片描述
    使用场景:Iterable<*>.filterIsInstance() 标准函数,通过制定类型实参,表明你需要过滤的类型
    在这里插入图片描述
    注意:虽然filterIsInstance 函数被标记为inline,而不是期望的lambda作为实参,这样做的目的是为了嫩够使用实化类型参数
  3. 使用::class.java 获取java.lang.Class对应的Kotlin类,等价于Java中的name.class
  4. 实化类型参数使用场景:
    • 类型检查和类型转换(is, !is, as , as?)
    • 使用Kotlin反射API (::class)
    • 获取相应的java.lang.Class (::class.java)
    • 作为调用其他函数的类型实参

变型:泛型和子类型化

  1. 变型的重要性:既不会以不方便的方式限制用户,也不会破坏用户所期望的类型安全
  2. 在把一个列表传给期望Any对象的列表的时候,如果函数接收的是只读类型列表,可以传递更具体的元素类型的列表,如果列表是可变的,则不能这么做;
  3. 类,类型和子类型: 非泛型类,类的名称可以直接当做类型使用,一个Kotlin类都可以用于构造至少两种类型(列如String类,可以构造String, String?两种类型);List不是一个类型(是一个类),但List, List<String?>等都是它的合法的类型;每一个泛型类都可以生成潜在的无限数量的类型;
  4. 子类型: 任何时候如果需要的是类型A的值,你都能够使用类型B的值(当做A的值),类型B就称为类型A的子类型;超类型是子类型的反义词;
  5. 注意:
    在这里插入图片描述
  6. 协变:在Kotlin中,要声明类在某个类型参数上是可以协变的,在该类型参数的名称前加上out关键字;协变的作用:保留子类型化关系(例如:如果A是B的子类型,那么Producer 就是Producer 的子类型,我们就说子类型化被保留了,也就是协变 ) 协变相当于Java中的extends。
    在类成员的声明中类型参数的使用可以分为in位置和out位置 ,如果函数把类型参数T当成返回类型,就说它在out位置,如果T用作函数的参数类型,它就在in位置;
    在这里插入图片描述
    **注意:类型参数T上的关键字out有两层含义:
    1. 子类型化被保留(协变)
    2. T只能用在out位置(作为返回类型)**
      在这里插入图片描述
      构造方法的参数既不在in位置,也不在out位置
      位置规则的只覆盖了类外部可见(public, protected, internal)的API,变型规则只会防止外部使用者对类的误用但不会对类自己的实现起作用
  7. 逆变:逆变的概念可以看做协变的镜像,在类型参数的前面加上关键字in,,说明该类型参数只能在in的位置使用。如果B是A的子类型,那么Comsumer 就是 Comsumer 的子类型,类型参数A,B交换了位置。我们就说子类型化被反转了。逆变相当于Java中的super。
    在这里插入图片描述
    协变与逆变的关系:
    在这里插入图片描述
    注意:
    在这里插入图片描述
    该函数在Kotlin中可以用表示法:(P)-> R,来表达Function1<in P, out R> 。P被in标记,只能用在参数上,R被out标记,只能用在返回值上。
  8. 点变型: 每一次使用带类型参数的类型的时候,可以指定这个类型参数是否可以使用它的子类型还是超类型替换,这就叫做点变型。与协变、逆变相互斥,不能同时存在。
    在这里插入图片描述
    类型投影
    在这里插入图片描述
    source的类型参数只能用在out位置(返回值)。如果类型参数已经有out变型,获取它的out投影则没有任何意义。就像List 和List是一个意思,因为List已经声明成了class List
    在这里插入图片描述
    在这里插入图片描述
  9. 星号投影:使用*代替类型参数 表示不知道关于泛型实参的任何信息。使用场景:当确切的类型实参是未知的或者是不重要的的时候,可以使用星号投影。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值