Kotlin学习之 12泛型

104 篇文章 14 订阅
15 篇文章 0 订阅

Kotlin 泛型

泛型,即 “参数化类型”,将类型参数化,可以用在类,接口,方法上。

与 Java 一样,Kotlin 也提供泛型,为类型安全提供保证,消除类型强转的烦恼。

一.声明一个泛型类:


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

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


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

2.以下实例向泛型类 Box 传入整型数据和字符串:


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

fun main(args: Array<String>) {
    var boxInt = Box<Int>(10)//等同于 var boxInt = Box(10)
    var boxString = Box<String>("Runoob")

    println(boxInt.value)
    println(boxString.value)
}
输出结果为:
10
Runoob

定义泛型类型变量,可以完整地写明类型参数,如果编译器可以自动推定类型参数,也可以省略类型参数。

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


fun <T> boxIn(value: T) = Box(value)

// 以下都是合法语句
val box4 = boxIn<Int>(1)
val box5 = boxIn(1)     // 编译器会进行类型推断

4.在调用泛型方法时,如果可以推断出类型参数,可以省略泛型参数。

以下实例创建了泛型方法 doPrintln,方法根据传入的不同类型做相应处理:

fun main(args: Array<String>) {
    val age = 23
    val name = "runoob"
    val bool = true

    doPrintln(age)    // 整型
    doPrintln(name)   // 字符串
    doPrintln(bool)   // 布尔型
}

fun <T> doPrintln(content: T) {

    when (content) {
        is Int -> println("整型数字为 $content")
        is String -> println("字符串转换为大写:${content.toUpperCase()}")
        else -> println("T 不是整型,也不是字符串")
    }
}
输出结果为:

整型数字为 23
字符串转换为大写:RUNOOB
T 不是整型,也不是字符串

二.泛型约束

我们可以使用泛型约束来设定一个给定参数允许使用的类型。

Kotlin 中使用 : 对泛型的的类型上限进行约束。

1.最常见的约束是上界(upper bound):


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

Comparable 的子类型可以替代 T。 例如:


sort(listOf(1, 2, 3)) // OK。Int 是 Comparable<Int> 的子类型
sort(listOf(HashMap<Int, String>())) // 错误:HashMap<Int, String> 不是 Comparable<HashMap<Int, String>> 的子类型

2.默认的上界是 Any?

对于多个上界约束条件,可以用 where 子句:

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

上面泛型约束看得我真的是一脸懵逼!
对于这些陌生的代码和说明,如果没有运行实例真是不想去想。会有不少想不通的地方!
可能也是因为我在java中使用泛型的实例不多!特别是多条件约束基本没自己写过!

这里只给一个泛型约束的简单示例:

3.泛型约束示例

//泛型约束 <占位符:类型>
fun <T : Comparable<T>> sort(list: List<T>) {
    // ……
    println("sort method:"+list[0])
    //叫我写排序还可以,但是叫我写判断list里面的数据类型,我也不知道怎么搞!
    //如果只是用is判断好像不行!还会报错!
}

 fun main(args: Array<String>) {
        val list=listOf(1,2,3,8,4)
        sort(list)
        println(list)

        val list2=listOf("hello","world")
        sort(list2)
        println(list2)

}

代码运行结果:

sort method:1
[1, 2, 3, 8, 4]
sort method:hello
[hello, world]

三.型变

Kotlin 中没有通配符类型,它有两个其他的东西:声明处型变(declaration-site variance)与类型投影(type projections)。

1.声明处型变

声明处的类型变异使用协变注解修饰符:in、out,消费者 in, 生产者 out。

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


// 定义一个支持协变的类
class Runoob<out A>(val a: A) {
    fun foo(): A { //out类型不能作为方法的参数
        return a
    }
}

fun main(args: Array<String>) {
    var strCo: Runoob<String> = Runoob("a")     
    var anyCo: Runoob<Any> = Runoob<Any>("b")   
    anyCo = strCo                               //数据赋值
    println(anyCo.foo())   // 输出 a
}

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


// 定义一个支持逆变的类
class Runoob<in A>(a: A) {
    fun foo(a: A) {   //in类型能做参数,不能作为返回的值
        println(a)    //这里a对象是参数对象,不是类的a对象,并且这个方法里面得不到类的参数对象a,不知道为啥?
    }
}

fun main(args: Array<String>) {
    var strDCo = Runoob("a")
    var anyDCo = Runoob<Any>("b")
    strDCo = anyDCo
    strDCo.foo("bb") //最终输出bb
}

这里,我并没有感觉到in、out有啥大的作用和意义。。。

2.星号投射

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

对于这个问题, 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<in Nothing, String> ;
Function<Int, *> , 代表 Function<Int, out Any?> ;
Function<, > , 代表 Function<in Nothing, out Any?> .

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

本文介绍了关于Kotlin的泛型,后面有些知识还是比较难懂!
鉴于java使用泛型都是不是很多,建议大家读懂上面第一大点的内容:泛型的声明,就可以了。

上面的示例代码也可以在线测试的:https://blog.csdn.net/wenzhi20102321/article/details/79859347

共勉:苦难能提高我们生命的质量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

峥嵘life

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值