美文网首页
Kotlin 泛型

Kotlin 泛型

作者: zhongjh | 来源:发表于2021-11-03 15:05 被阅读0次

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

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

    声明一个泛型类:

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

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

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

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

    class Box<T>(t : T) {
        var value = t
    }
    
    fun main(args: Array<String>) {
        var boxInt = Box<Int>(10)
        var boxString = Box<String>("Runoob")
    
        println(boxInt.value)
        println(boxString.value)
    }
    

    输出结果为:

    10
    Runoob
    

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

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

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

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

    以下实例创建了泛型函数 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 不是整型,也不是字符串
    

    泛型约束

    简单来说就是约束某个类型

    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>> 的子类型
    

    如果想约束多个条件,可以用 where 子句:

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

    型变

    Kotlin 中没有通配符类型,即是以下这个:

    public static void test3(List<?> c){
      for (int i = 0; i < c.size(); i++) {
           System.out.println(c.get(i));
       }
    }
    

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

    声明处型变

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

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

    // 定义一个支持协变的类
    class Runoob<out A>(val a: A) {
        fun foo(): A {
            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
    }
    
    声明处型变
    // 定义一个支持逆变的类
    class Runoob<in A>(a: A) {
        fun foo(a: A) {
        }
    }
    
    fun main(args: Array<String>) {
        var strDCo = Runoob("a")
        var anyDCo = Runoob<Any>("b")
        strDCo = anyDCo
    }
    

    星号投射

    1.概述

    <*>用来表示不知道关于泛型实参的任何信息

    2.结论

    <> 星型投影,修饰的容器(比如:MutableList,MutableMap ),只能读不能写。 相当于<out Any?>
    比如:MutableList<
    > 表示的是 MutableList<out Any?>

    3.解释

    <*>星号投射,表示“不知道关于泛型实参的任何信息”,若在修饰容器时,因为不知道是哪个类型,所以并不能向容器中写入任何东西(写入的任何值都可能会违反调用代码的期望)。读容易的值是可以的,因为所有存储在列表中的值都是Any?的子类

    4.示例
        fun main() {
            val list = mutableListOf<String>()
            list.add("1")
            list.add("2")
            reportInfo(list)
        }
    
        fun reportInfo(info: MutableList<*>) {
            var test = Integer.valueOf(info[0].toString())
            test += 10
            tvContent.append(test.toString())
            tvContent.append("+")
            tvContent.append(info[1].toString())
        }
    
        fun reportInfoError(info: MutableList<*>) {
            // 这样是编译错误的
    //        info.add("3")
        }
    

    相关文章

      网友评论

          本文标题:Kotlin 泛型

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