美文网首页
每天学一点 Kotlin -- 多彩的类:泛型

每天学一点 Kotlin -- 多彩的类:泛型

作者: 冯可乐同学 | 来源:发表于2021-11-21 10:21 被阅读0次

----《第一季Kotlin崛起:次世代Android开发 》学习笔记

总目录:每天学一点 Kotlin ---- 目录
上一篇:每天学一点 Kotlin -- 多彩的类:密封类
下一篇:每天学一点 Kotlin -- 多彩的类:嵌套类

1. 泛型

1.1 Kotlin 中也有泛型的概念,和 Java 中的类似,但又不尽相同。先来看两种场景
(1) 第一种场景:

fun testCommon01(value: Int) {
    println("the value is $value")
}

fun testCommon01(value: Double) {
    println("the value is $value")
}

fun testCommon01(value: String) {
    println("the value is $value")
}

fun main() {
    testCommon01(10)
    testCommon01(10.11)
    testCommon01("11.01")
}

打印结果:

the value is 10
the value is 10.11
the value is 11.01

上面的代码中,重载貌似也看着很方便实现,但是还是写了大量重复的代码。

(2) 第二种场景:

fun<T> testCommon02(value: T) {
    println("testCommon02() -- the value is $value")
}

fun main() {
    testCommon02(10)
    testCommon02(10.11)
    testCommon02("11.01")
}

打印结果:

testCommon02() -- the value is 10
testCommon02() -- the value is 10.11
testCommon02() -- the value is 11.01

1.2 如上面第二种场景所示:通过一个占位符,来匹配不同类型,这种形式就是泛型。

2. 基本使用

2.1 通常我们会在类、接口、方法中声明泛型:
2.1.1 泛型类

class Animal<T> {}
// 或者
class Animal<T>(par: T) {}

2.1.2 泛型接口

interface IAnimal<T> {}

2.1.3 泛型方法

fun<T> initAnimal(param: T) {}

2.2 在上面的第二种场景中,使用了泛型方法,下面学习泛型类和泛型接口
2.2.1 使用泛型类:

class TestCommonData1<T>(value: T) {
    var v = value
    fun printInfo() = println("TestCommonData1<T>(value: T)  --- value = $v")
}

fun main() {
    val tc1 = TestCommonData1<Int>(100)
    tc1.printInfo()
    val tc2 = TestCommonData1<Double>(100.001)
    tc2.printInfo()
    val tc3 = TestCommonData1<String>("100-100")
    tc3.printInfo()
}

打印结果:

TestCommonData1<T>(value: T)  --- value = 100
TestCommonData1<T>(value: T)  --- value = 100.001
TestCommonData1<T>(value: T)  --- value = 100-100

2.2.2 使用泛型接口:

interface TestCommonInterface1<T> {
    fun printMsg(msg: T)
}

class TestCommonData2 : TestCommonInterface1<Int> {
    override fun printMsg(msg: Int) {
        println("TestCommonData2() -- msg = $msg")
    }
}

class TestCommonData3 : TestCommonInterface1<String> {
    override fun printMsg(msg: String) {
        println("TestCommonData3() -- msg = $msg")
    }
}

fun main() {
    val tc2 = TestCommonData2()
    val tc3 = TestCommonData3()
    tc2.printMsg(200)
    tc3.printMsg("300-300")
}

打印结果:

TestCommonData2() -- msg = 200
TestCommonData3() -- msg = 300-300

上述使用泛型接口中,跟 1.1 中第一种场景相似,也是写了很多重复的代码。所以可以把泛型接口中的代码进行优化:

class TestCommonData4<T> : TestCommonInterface1<T> {
    override fun printMsg(msg: T) {
        println("TestCommonData3() -- msg = $msg")
    }
}

fun main() {
    val tc4_1 = TestCommonData4<Int>()
    tc4_1.printMsg(40)
    val tc4_2 = TestCommonData4<Double>()
    tc4_2.printMsg(40.04)
    val tc4_3 = TestCommonData4<String>()
    tc4_3.printMsg("40-40")
}

打印结果:

TestCommonData3() -- msg = 40
TestCommonData3() -- msg = 40.04
TestCommonData3() -- msg = 40-40

3. out 关键字:

3.1 先来看一段代码:

interface TestCommonInterface2<T> {
    fun create(): T
}

//fun change2(f: TestCommonInterface2<String>) {
//    val factoryAny: TestCommonInterface2<Any> = f  // 错误的写法
//}

这段代码理论上是没有问题的,但是其实编译不通过。因为这种情况下如果 T 作为函数的参数是不可以的,因为 T 对参数的类型做了更多的限制,而 Any 太宽泛了。然而,编译器并不知道我们的 T 只是用在方法的返回值中。也就是说,其实在这里,Factory<T> 转变为 Factory<String> 是没有问题的,只是编译器多虑了。那么如何让编译器信任我们呢,可以把代码改成如下:

interface TestCommonInterface3<out T> {
    fun create(): T
}

fun change3(f: TestCommonInterface3<String>) {
    val factoryAny: TestCommonInterface3<Any> = f
}

即,只要在参数T前面加上 out 标识符,这是在告诉编译器这个类型只是用在输出上,也就是方法的返回值,并没有用在任何输入的情况中,这样编译就通过了。

4 in 关键字

4.1 先来看一段代码:

interface Factory<T>{
    fun produce(ele: T)
}

fun change(f: Factory<Any>){
    val factory_any: Factory<Any> = f
}

4.2 上面这段代码仍然是在编译时不会通过的。但是理论上是可以的,因为 String 根本还是继承了 Any 这个类,我们原本用 Any 作为参数类型,当然 String 也是符合这个参数类型的,只是参数做了更多的限制。因此,这种转变在这种情况下是完全没有问题的,因为在接口中,我们把 T 用在了输入中。如果任然想要编译器信任我们,需要把代码改为以下:

interface Factory<in T>{
    fun produce(ele: T)
}

即,在参数前面加了 in 标识符,也就是告诉编译器我们的 T 只会是输入。

5. 总结

5.1 其实在 Kotlin 中,in 和 out 不止告诉编译器这个类型存在的位置,也分别有一个好听的名称,就是“消费者”和“生产者”。

5.2 限制类型:因为 T 这个通配符是包罗万象的,代表了所有的类型。但是在有些情况下并不是所有的类型都适合的,比如说一个实现计算器功能的方法,T 应该是 Int,Double,Float 等这些可以进行运算的类。所以我们要对 T 这个类型做一些约束以实现一些功能。比如约束 T 已经要实现某些接口或继承某个类。

相关代码:https://gitee.com/fzq.com/test-demo

相关文章

  • 每天学一点 Kotlin -- 多彩的类:泛型

    1. 泛型 1.1 Kotlin 中也有泛型的概念,和 Java 中的类似,但又不尽相同。先来看两种场景(1) 第...

  • 泛型

    与Java泛型相同,Kotlin同样提供了泛型支持。对于简单的泛型类、泛型函数的定义,Kotlin 与 Java ...

  • Kotlin---泛型

    Kotlin不变型泛型 Kotlin的不变型泛型和Java一样,通过声明泛型类型来使用泛型类。而该种泛型声明后,则...

  • kotlin--泛型

    kotlin作为一种高级语言,也提供了泛型,它的泛型比Java更为灵活 一、泛型类 1.定义泛型类 定义泛型类和J...

  • Kotlin 泛型

    Kotlin 泛型 1. 泛型类 定义一个泛型类 使用 在继承中 使用 2. 泛型函数 使用 3. 泛型的擦除 无...

  • Kotlin 泛型

    Kotlin 支持泛型, 语法和 Java 类似。例如,泛型类: 泛型函数: 类型变异 Java 的泛型中,最难理...

  • 泛型

    Kotlin 泛型详解 声明一个泛型类 声明一个泛型方法 泛型约束 List 和 List 是...

  • Kotlin泛型

    Kotlin 泛型 声明泛型类 和Java一样,Kotlin通过在类名称后面加上一对尖括号,并把类型参数放在尖括号...

  • 每天学一点 Kotlin -- 多彩的类:数据类

    1. 数据类 1.1 在 Kotlin 中,有时候仅仅只需要一些数据而不需要调用方法,那么会将这些数据单独放在一个...

  • 每天学一点 Kotlin -- 多彩的类:密封类

    1. 密封类 1.1 密封类主要在里面封装了各种各样的类,也就是说,它是一个大容器,在里面放了一个又一个的类元素,...

网友评论

      本文标题:每天学一点 Kotlin -- 多彩的类:泛型

      本文链接:https://www.haomeiwen.com/subject/wlytaltx.html