Kotlin 泛型

泛型,即 "参数化类型",将类型参数化,可以用在类,接口,方法上。
与 Java 一样,Kotlin 也提供泛型,为类型安全提供保证,消除类型强转的烦恼。
声明一个泛型类:

class Box(t: T) {
    var value = t
}

创建类的实例时我们需要指定类型参数:

val box: Box = Box(1)
// 或者
val box = Box(1) // 编译器会进行类型推断,1 类型 Int,所以编译器知道我们说的是 Box

Kotlin 泛型函数的声明与 Java 相同,类型参数要放在函数名的前面:

class Box(t: T) {
    var value = t
}
//fun  boxIn(b:T) = Box(b) //==这个等价于下面这个函数
fun  boxIn(b: T): Box {
    return Box(b)
}
fun main() {
    val box = Box(1)
    println(box.value)
    val box1 = Box("箱子")
    println(box1.value)
    val boxIn = boxIn("你妹的")
    println(boxIn.value)
    println(fun0(88))
}
fun  fun0(t:T): String {
    return "泛型$t"
}

泛型约束 与java类似

我们可以使用泛型约束来设定一个给定参数允许使用的类型。
Kotlin 中使用 : 对泛型的类型上限进行约束。
最常见的约束是上界(upper bound):
举个栗子

fun > sort(list: List) {
    // ……
}

Comparable的子类型可以替代 T。 例如:
sort(listOf(1, 2, 3)) // OK。Int 是 Comparable 的子类型
sort(listOf(HashMap())) // 错误:HashMap 不是 Comparable> 的子类型
默认的上界是 Any?。
对于多个上界约束条件,可以用 where 子句:

fun  copyWhenGreater(list: List, threshold: T): List
    where T : CharSequence,
          T : Comparable {
    return list.filter { it > threshold }.map { it.toString() }
}

型变

Kotlin 中没有通配符类型,它有两个其他的东西:声明处型变(declaration-site variance)与类型投影(type projections)。
声明处型变
声明处的类型变异使用协变注解修饰符:in、out,消费者 in, 生产者 out。
使用 out 使得一个类型参数协变,协变类型参数只能用作输出,可以作为返回值类型但是无法作为入参的类型:

// 定义一个支持协变的类
class Runoob(val a: A) {
    fun foo(): A {
        return a
    }
}

fun main(args: Array) {
    var strCo: Runoob = Runoob("a")
    var anyCo: Runoob = Runoob("b")
    anyCo = strCo
    println(anyCo.foo())   // 输出 a
}

in 使得一个类型参数逆变,逆变类型参数只能用作输入,可以作为入参的类型但是无法作为返回值的类型:

// 定义一个支持逆变的类
class Runoob(a: A) {
    fun foo(a: A) {
    }
}

fun main(args: Array) {
    var strDCo = Runoob("a")
    var anyDCo = Runoob("b")
    strDCo = anyDCo
}

星号投射 没看懂

有些时候, 你可能想表示你并不知道类型参数的任何信息, 但是仍然希望能够安全地使用它. 这里所谓"安全地使用"是指, 对泛型类型定义一个类型投射, 要求这个泛型类型的所有的实体实例, 都是这个投射的子类型。

对于这个问题, Kotlin 提供了一种语法, 称为 星号投射(star-projection):

假如类型定义为 Foo , 其中 T 是一个协变的类型参数, 上界(upper bound)为 TUpper ,Foo<> 等价于 Foo . 它表示, 当 T 未知时, 你可以安全地从 Foo<> 中 读取TUpper 类型的值.
假如类型定义为 Foo , 其中 T 是一个反向协变的类型参数, Foo<> 等价于 Foo . 它表示, 当 T 未知时, 你不能安全地向 Foo<> 写入 任何东西.
假如类型定义为 Foo , 其中 T 是一个协变的类型参数, 上界(upper bound)为 TUpper , 对于读取值的场合, Foo<*> 等价于 Foo , 对于写入值的场合, 等价于 Foo .

如果一个泛型类型中存在多个类型参数, 那么每个类型参数都可以单独的投射. 比如, 如果类型定义为interface Function , 那么可以出现以下几种星号投射:

Function<*, String> , 代表 Function ;
Function , 代表 Function ;
Function<, > , 代表 Function .

注意: 星号投射与 Java 的原生类型(raw type)非常类似, 但可以安全使用

笔记

关于星号投射,其实就是*代指了所有类型,相当于Any?
给文中补个例子方便理解:

class A(val t: T, val t2 : T, val t3 : T)
class Apple(var name : String)
fun main(args: Array) {
    //使用类    
    val a1: A<*> = A(12, "String", Apple("苹果"))
    val a2: A = A(12, "String", Apple("苹果"))   //和a1是一样的
    val apple = a1.t3    //参数类型为Any
    println(apple)
    val apple2 = apple as Apple   //强转成Apple类
    println(apple2.name)
    //使用数组
    val l:ArrayList<*> = arrayListOf("String",1,1.2f,Apple("苹果"))
    for (item in l){
        println(item)
    }
}

使用星号投射可以保证安全的使用未知类型,这个是java做不到的

你可能感兴趣的:(Kotlin 泛型)