学习目标:
学习泛型,应用场景,学以致用
学习内容:
1.泛型的基本概念
2.泛型约束
3.泛型的形变
4.UnsafeVariance
5.星投影Start Projection
6.泛型的实原理与内联特化
学习时间:
时间就像海绵里的水,挤一挤总会有的
学习开始:
第一节:泛型的基本概念
1.泛型的概念
- 泛型是一种类型层面的抽象
- 泛型通过泛型参数实现构造更加通用的类型的能力
- 泛型可以让符合继承关系的类型批量实现某些能力
//函数生命泛型
fun <T>maxOf(a:T , b:T):T {
return a
}
//类声明泛型
class List<T>
泛型参数 分为 形参 和 实参
生明的时候 是 形参
泛型参数实例化的 是后是实参
第二节:泛型的约束
第一节中 maxof 无法比较字符串大小 ,这时我们可以使用泛型约束
让 泛型参数 继承自 Comparable 来实现字符串的比较
fun main() {
val max = maxOf("Hello","World")
println(max)
}
fun <T:Comparable<T>>maxOf(a:T , b:T) :T{
return if (a>b) a else b
}
我们在泛型中 不仅可以 约束 ,还可以实现多个约束 使用 where 语句
多个泛型参数
在java中定义多个约束 使用的是 交叉类型 a&b
//多个约束
fun <T> callMax(a :T , b:T) where T:Comparable<T> , T:() -> Unit{
if (a >b) a() else b()
}
//多个泛型参数
fun <T , R> callMax(a:T,b:T):R where T:Comparable<T> , T:() -> R{
return if (a >b) a() else b()
}
//多个泛型参数 可以添加多个约束
fun <T , R> callMax(a:T,b:T):R where T:Comparable<T> , T:() -> R ,R:Number{
return if (a >b) a() else b()
}
第三节:泛型的形变
泛型实参的继承关系对泛型类型的继承关系的影响
1.形变
协变 :教辅书店可以买到 教辅书籍和书 ,书店可以买书 不一定买到教辅书籍
教辅书籍 是书集的子类
open class A
class B : A()
fun main() {
// Kotlin 中的泛型是默认不型变的,无法自动完成类型转换
val array1: Array<B> = arrayOf(B(), B(), B())
val array2: Array<A> = array1 //这行代码 会报错
}
B继承子A
A是B的父类
Array<A> 就是 Array<B> 的父类
理论上 Array<A> = Array<B> 是合法的 安全的
Koltin中 泛型是默认不形变的 ,无法自动完成类型转换
val list1: List<B> = listOf(B(), B(), B())
val list2: List<A> = list1
list是可以的 我们 看源码分析一下
public class Array<T> {}
public interface List<out E> : Collection<E> {}
通过源码可以得知 List泛型参数中使用了out修饰符
这就是kotlin中用来像编译器解释这种情况
关于
out
修饰符我们可这样理解,当类、接口的泛型类型参数被声明为out
时,则该类型参数是协变
的,泛型类型的子类型是被保留的,它只能出现在函数的输出位置,只能作为返回类型,即生产者
。带来的好处是,A
是B
的父类,那么List<A>
可以是List<B>
的父类
下面我们通过书店和 教辅书店的 案例 来学习
package com.junbao
//定义书的接口
interface Book
//定义教辅书的接口
interface EduBook :Book
//定义书店类
//BookStore 接收一个 协变的泛型参数 参数T约束是Book 所以一定是 Book类型的
class BookStore<out T:Book>{
//T 是协变点 返回 T
fun getBook():T{
TODO()
}
}
fun covariant(){
//在书店里面 找教辅书籍 书店可以 找教辅书籍
val eduBookStore:BookStore<EduBook> = BookStore<EduBook>()
//教辅书店 就是 书店
val bookStore:BookStore<Book> = eduBookStore;
val book:Book = bookStore.getBook()
val edubook:EduBook = eduBookStore.getBook()
//子类 可以拿 父类的 父类不能拿子类的
val book1:Book= eduBookStore.getBook()
}
- 子类Derived兼容父类Base
- 生产者Producer<Deriverd>兼容Producer<Base>
- 存在协变点的类的泛型参数必须声明为协变或者不变
- 当泛型类作为泛型参数类实例的生产者时用协变
2.逆变
val list1: List<A> = listOf(A(), A(), A())
val list2: List<B> = list1
反过来 因为 List<B> 不是 List<A>的父类 所以 上面代码错
这时候 我们 可以通过in修饰符 来解决这一问题
逆变就是 和形变相反
关于
in
修饰符我们可这样理解,当类、接口的泛型类型参数被声明为in
时,则该类型参数是逆变
的,泛型类型的父类型是被保留的,它只能出现在函数的输入位置,作为参数,只能作为消费类型,即消费者
。
package com.junbao
open class Waste
class DryWaste:Waste()
class Dustbin<in T:Waste>{
fun put(t:T){
TODO()
}
}
fun contravariant(){
val dustbin:Dustbin<Waste> = Dustbin<Waste>()
val dryWasteDustbin:Dustbin<DryWaste> = dustbin
val waste = Waste()
val dryWaste = DryWaste()
dustbin.put(waste)
dustbin.put(dryWaste)
//
dryWasteDustbin.put(waste)
dryWasteDustbin.put(dryWaste)
}
逆变
- 子类Derived兼容父类Base
- 消费者Consumer<Base> 兼容 Consumer<Derived>
- 存在逆变点的类的泛型参数必须声明为逆变或不变
- 当泛型类做为泛型参数类实例的消费者时用逆变
第四节:UnSaveVariance
协变里面 出现了 逆变点 是不合法的 违反了原则
sealed class List<out T>{
//@UnsafeVariance 通过它 来修饰这块是安全的 自己来保证
operator fun contains(t:@UnsafeVariance T):Boolean{
return false
}
}
可以通过@UnsafeVariance 来保证这块是安全的 ,编译器 可以不用管
违反形变约束是不安全的
违反形变约束的安全前提:
- 即声明的协变的类出现逆变点或相反
- 声明为不变的类接收逆变或协变的类型参数
安全前提:
- 泛型参数协变,逆变点不能引起修改,即始终只读不写
- 泛型参数逆变,协变点不得外部获取,即始终只写不读
第五节:星投影
‘*’可用在变量类型声明的位置
‘*’可用以描述一个未知的类型
‘*’所替换的类型在:
·协变点返回泛型参数上限类型
·逆变点接收泛型参数下限类型
协变点
//协变点
class QueryMap<out K:CharSequence , out V:Any>{
fun getKey():K = TODO()
fun getValue():V = TODO()
}
fun main() {
val queryMap: QueryMap<*,*> = QueryMap<String , Int>()
queryMap.getKey() //上限 CharSequence
queryMap.getValue() //上限 Any
}
使用的位置 使用在类型声明的位置 不能使用在后面
逆变点
class Function<in P1 , in P2>{
fun invoke(p1:P1 ,p2:P2) =Unit
}
val f:Function<*,*> = Function<Number , Any>()
f.invoke() //Nothing
逆变点取下线
kotlin中定义泛型是没有办法加下线的约束的
所以对于所有类型他的下线只有一种Nothing
上面代码的invoke没有办法调用
因为Nothing没有实例
星投影的适用范围
“*”不能直接或者间接应用在属性或函数上
- QueryMap<String , *>() 间接作用到属性上
- maxOf<*>(1,3) 直接作用到函数上
星投影可以用来描述一个类型
val queryMap: QueryMap<*,*>
- if(f is Function<*,*>){...}
- HashMap<String , List<*>>()
第六节:泛型实现原理与内联特化
下回补充吧