Kotlin泛型:泛型约束、协变逆变、星投影

1、泛型概念

在这里插入图片描述

//方法声明泛型
fun <T> test1(a: T, b: T) {
    
    }
fun <T, R> test2(a: T, b: R) {
    
    }

//类声明泛型
class Test<T> {
    
    
    fun test(t: T) {
    
    }
}

2、泛型约束

//泛型约束
//约束T必须是Comparable的子类,必须实现了Comparable接口
//实现了Comparable接口才能比较大小
fun <T : Comparable<T>> maxOf(a: T, b: T) = if (a > b) a else b
//多个约束
//利用where设置多个约束
fun <T, R> callMax(a: T, b: T): R
        where T : Comparable<T>,
              T : () -> R,
              R : Number {
    
    
    return if (a > b) a() else b()
}

//T : () -> R  表示T是一个没有参数的方法,返回值是Number类型
val result: Double = callMax({
    
     3.14 }, {
    
     2.71 })

3、协变逆变

在这里插入图片描述

3.1 协变

//协变 out
//协变点:泛型参数作为方法返回值
//存在协变点的类的泛型参数必须声明为协变或不变
interface Book
interface EducationBook : Book

class BookStore<out T : Book> {
    
    
    fun getBook(): T {
    
    
        TODO()
    }
}

fun buyBook() {
    
    
    //父类:Book          子类:EducationBook
    //父类:bookStore   子类:educationBookStore
    //子类可以赋值给父类
    val educationBookStore: BookStore<EducationBook> = BookStore()
    val bookStore: BookStore<Book> = educationBookStore

    val book: Book = bookStore.getBook()
    val educationBook: EducationBook = educationBookStore.getBook()

    //子类中可以获取到父类的值
    val book2: Book = educationBookStore.getBook()
}

3.2 逆变

//逆变 in
//逆变点:泛型参数作为方法的形参
//存在逆变点的类的泛型参数必须声明为逆变或不变
open class Fruit
class Apple : Fruit()

class Person<in T : Fruit> {
    
    
    fun eat(t: T) {
    
    

    }
}

fun eatFruit() {
    
    

    //父类:Fruit                  子类:Apple
    //父类:personEatApple    子类:personEatFruit
    val personEatFruit: Person<Fruit> = Person()
    val personEatApple: Person<Apple> = personEatFruit


    personEatApple.eat(Fruit()) //错误
    personEatApple.eat(Apple())

    //子类personEatFruit可以替换所有类型
    personEatFruit.eat(Fruit())
    personEatFruit.eat(Apple())
}

3.3 举例

//协变举例
//stuId的T是协变点, 因为T是属性getter()的返回值
data class Student<out T>(
    val stuId: T
)


class Teacher<out N, out A> {
    
    
    private var name: N? = null
    private var age: A? = null
}

3.4 UnsafeVariance

在这里插入图片描述

协变点逆变

class Test<out T> {
    
    
    //协变
    private var param: T? = null
    fun getParam(): T? = param


    //协变点逆变, 只写不读
    fun setParam(t: @UnsafeVariance T) {
    
    
        param = t
    }
}

逆变点协变

class Test<in T> {
    
    
    //逆变
    private var param: T? = null
    fun setParam(t: T) {
    
    
        param = t
    }


    //逆变点协变, 只读不写
    fun getParam(): @UnsafeVariance T? {
    
    
        return param
    }
}

4、星投影

  • ‘*’ 可用在变量类型声明的位置
  • ‘*’ 可用以描述一个未知的类型

‘*’ 所替换的类型在:

  • 协变点 返回 泛型参数上限类型 (协变上限)
  • 逆变点 接收 泛型参数下限类型 (逆变下限)
    在这里插入图片描述

协变点

//父类
open class Fruit
//子类
class Apple() : Fruit()

//父类
open class Animal
//子类
class Cat() : Animal()


//定义
class MyHouse<out T1 : Fruit, out T2 : Animal> {
    
    
    private var thing1: T1? = null
    private var thing2: T2? = null

    fun getThing1(): T1? = thing1
    fun getThing2(): T2? = thing2
}


//使用
fun main() {
    
    
    val myHouse: MyHouse<*, *> = MyHouse<Apple, Cat>()
    val t1 = myHouse.getThing1() //t1是Fruit类型, 因为Apple的上限是Fruit
    val t2 = myHouse.getThing2() //t2是Animal类型, 因为Cat的上限是Animal
}

逆变点

//逆变下限是Nothing,Nothing是无法实例化的
class Test<in T1, in T2> {
    
    
    private var param1: T1? = null
    private var param2: T2? = null

    fun set(t1: T1, t2: T2) {
    
    
        param1 = t1
        param2 = t2
    }
}

fun main() {
    
    
    val test: Test<*, *> = Test<String, String>()
    test.set("param1", "param2") //错误, Nothing类型不可以实例化,不可以用"param1", "param2"对其赋值
}

星投影适用范围

//不可以直接或间接应用在属性或函数上
val map = HashMap<String, *>() //错误
val max = maxOf<*>(1, 2, 3) //错误


//适用于作为类型描述场景
val map2: HashMap<*, *> = HashMap<String, Int>()
map2.put("leon", 26) //错误, 逆变下限Nothing类型无法赋值String,Int

val f: Function2<Int, Int, String> = {
    
     a, b ->
    "The sum of $a and $b is ${
      
      a + b}"
}
if (f is Function2<*, *, *>) {
    
    
    f.invoke(1, 2)
}
if (f is Function) {
    
    
    f.invoke(1, 2)
}

val map3 = HashMap<String, List<*>>() //因为kotlin不支持高阶类型, 所以不管*传的是什么, 对HashMap而言都是List

猜你喜欢

转载自blog.csdn.net/weixin_41733225/article/details/129968399