java泛型特化,Kotlin笔记-029泛型

泛型

泛型基本声明方法

// 函数声明泛型

fun action(x: T, y: T): T {}

// 类声明泛型

class Action{}

泛型的约束

// 泛型的约束:让泛型实现接口或者继承某个类

fun > maxOf(x: T, y: T): T {

return if (x > y) x else y

}

fun main() {

val max: String = maxOf("Kotlin1", "Kotlin2")

println("max $max")

}

也可以对泛型多个约束:

// 泛型多个约束;使用 where 继承或实现多个接口或类型

fun callMax(x: T, y: T) where T : Comparable, T : () -> Unit {

if (x > y) x() else y()

}

以及多个泛型参数:

// 多个泛型参数;比如 Map 就是多个泛型参数

fun callMax2(x: T, y: T): R where T : Comparable, T : () -> R {

return if (x > y) x() else y()

}

泛型的型变

泛型的型变包括协变和逆变

fun main() {

// Java 的 PECS 法则:「Producer-Extends, Consumer-Super」

// extends> 作为生产者向外提供数据被消费

// Java 泛型通配符 ? extends 使泛型支持协变,但是「只能读取不能修改」,这里的修改仅指对泛型集合添加元素,如果是 remove(int index) 以及 clear 当然是可以的。

// super> 作为消费者只能用来添加数据(消费数据)

// Java 泛型通配符 ? super 使泛型支持逆变,但是「只能修改不能读取」,这里说的不能读取是指不能按照泛型类型读取,你如果按照 Object 读出来再强转当然也是可以的。

// 使用关键字 out 来支持协变,等同于 Java 中的上界通配符 ? extends

// 相当于 Java 中 List extends TextView>

var textViews: List

// 使用关键字 in 来支持逆变,等同于 Java 中的下界通配符 ? super

// 相当于 Java 中 List super TextView>

var textViews2: List

}

协变点与逆变点

协变点:函数返回值类型为泛型参数为协变点

interface Orange

class OrangeStore {

// 协变点

fun getOrange(): T {

TODO()

}

}

逆变点:函数参数类型为泛型参数为逆变点

interface Orange

class OrangeStore {

// 逆变点

fun getOrange(x: T) {

TODO()

}

}

UnsafeVariance

interface Orange

class OrangeStore {

// 协变参数,但是用了逆变点,违反了型变约束

// 使用 UnsafeVariance 排除编译器报错,让用户自己处理型变

fun getOrange(x: @UnsafeVariance T) {

TODO()

}

}

星投影:*

* 描述一个未知类型,可用在变量类型声明的未知

* 替换的类型如果是协变点,协变点返回泛型参数上限类型

* 替换的类型如果是逆变点,逆变点接受泛型参数下限类型

星投影用于协变点

class Action {

fun getTypeA(): T {

TODO()

}

fun getTypeB(): R {

TODO()

}

}

fun main() {

// 协变点返回泛型参数上限类型

val action: Action = Action()

val type1: CharSequence = action.getTypeA()

val type2:Number = action.getTypeB()

}

星投影用于逆变点

class Action {

fun getType(x: T, y: R) {

TODO()

}

}

fun main() {

// 逆变点接受泛型参数下限类型;下限类型为 Nothing,因为 Nothing 是所有类型的子类

val action: Action = Action()

// 只能传入 Nothing,但是 Nothing 没有实例,方法无法调用

// action.getType(Nothing,Nothing)

}

星投影适用范围:

不能直接或间接应用在属性或者函数上,不能传给函数当参数或者类中当属性

适用于类型描述的场景

综上,星投影适用于声明处,不适用于定义处

内联特化:inline reified

Kotlin 泛型和 Java 泛型一样,都会发生类型擦除

比如 List 和 List,编译成字节码后,只留下 List 类型

而 C# 和 C++ 中的 List 和 List,编译后会生成 List 和 List两个类型

// 因为有类型擦除的限制,所以以下场景泛型类型无法当做真实类型

fun action(t:T){

val t = T() // 无法编译;T 类型的构造器未知

val tArray = Array(10, TODO()) // 无法编译;数组中类型不会擦除,但是类型未知

val tClass = T::class.java // 无法编译;类型被擦除,类型未知

val tList = ArrayList() // 可以编译;因为集合中反正会类型擦除

}

为了避免类型擦除的限制,Kotlin 使用内联特化来解决

// 内联特化之后,泛型类型可以当做真实类型

inline fun action(t:T){

// 比如外部调用该方法,传入参数为 String,那么 T 类型将特化成 String 类型

val t = T() // 仍然无法编译;T 类型的构造器未知

val tArray = Array(10, TODO()) // 可以编译;可以得到正确的类型

val tClass = T::class.java // 可以编译;可以得到正确的类型

val tList = ArrayList() // 可以编译;因为集合中还是会类型擦除

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值