泛型

QuibblerQuibbler 2020-06-14 约 7 分钟 1985 次阅读

泛型

类型参数化,和Java一样,Kotlin也有泛型。省去类型强转的麻烦,也提供了类型安全方面的保障。

1、泛型类

定义带泛型参数的类。

例:

class Type<T>(type: T) {
    var type: T = type
        get() {
            return field
        }
        set(value) {
            field = value
        }
}

泛型类Type中的值就会根据实际传入的参数类型

var bool = Type(false)
    println(bool.type.javaClass.simpleName)
    var str = Type("string")
    println(str.type.javaClass.simpleName)
    var num: Type<Int> = Type(1)
    println(num.type.javaClass.simpleName)

输出结果:

//boolean
    //String
    //int

注意:

①如果已经将某个变量初始化为一种泛型,就不能再次赋值成其它类型的泛型了:

②创建泛型类型的对象直接使用var关键字即可,Kotlin自动进行类型推断。如果一定要指定变量的类型,必须也要指定泛型的类型:

正确的泛型类型定义语法,加上具体的泛型类型:

var num: Type<Int> = Type(1)

2、泛型方法

方法的参数、返回值也可以是泛型,更加传入参数的值推断实际类型。

例:

定义一个泛型方法:

fun <T> whichType(type: T) {
    if (type is String) {
        println("String")
    } else if (type is Int) {
        println("Int")
    } else if (type is Boolean) {
        println(type)
    }
}

传入不同类型的参数,泛型函数根据传入的参数做不同的操作:

whichType(1)
    whichType(false)
    whichType("what?")
    /**
     * 输出:
     * Int
     * false
     * String
     */

上面一堆判空方法,能不能简写呢?比如直接简写成:

fun <T> whichType(type: T) {
    println(type.javaClass.simpleName)
}

这是不行的,非空才能调用泛型参数的方法

既然需要判空?,判空语法参考《 Kotlin基本语法》,就可以根据传入的参数类型做不同的操作:

fun <T> whichType(type: T) {
    println(type?.javaClass?.simpleName)
}

虽然不会提示错误。但是,实际编译运行却会报错:

Error:Kotlin: Type inference failed: Not enough information to infer parameter T in val <T : Any!> Class<T>.simpleName: String!

Please specify it explicitly.

Error:Kotlin: Type parameter bound for T in val <T : Any> T.javaClass: Class<T>

 is not satisfied: inferred type T is not a subtype of Any

提示意思是没有足够的信息推断类型T。

3、泛型限定

上面的问题可以很轻松的用泛型限定来解决:

fun <T : Any> whichType(type: T) {
    println(type.javaClass.simpleName)
}

但是这样泛型方法传入的泛型又不可以为null

在泛型限定T前加上?,表明该泛型参数可能为null。

fun <T : Any?> whichType(type: T) {
    //type可为null
}

看着似乎完美了,是因为还是空方法,加上点调用又警告了和上一节出现一样的情况,因为默认的上界是 Any?,加不加都一样。

那有没有办法呢?还是有的用?:表达式做判空断处理

fun <T : Any?> whichType(type: T) {
    println((type ?: 1).javaClass.simpleName)
}

4、协变

Java泛型中有通配符,这里不详细介绍,参考《java泛型通配符详解》。

<T extends View> void painting(T t) {
        //T可以是所有继承自View的子类
    }

4.1、声明处型变 out

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

例:

class Variance<out T>(val t: T) {
    fun variance(): T {
        return t
    }
}

协变返回

var a = Variance<String>("String")
    var b = Variance<Any>("Any")
    //可以将a赋值给b,返回类型将协变成为a中的泛型类型String
    b = a

4.2、类型投影 in

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

例:

class Projections<in T>( t: T) {
    fun projections() {
    }
}

输入协变

var a = Projections<String>("String")
    var b = Projections<Any>("Any")
    a = b

注意:

in和out不能同时使用,两者是互斥的

协变输入只能由泛型宽的转到泛型窄的,反过来不行。

学习资料:

        Kotlin 泛型

相关推荐

Parcelize:Parcelable 实现生成器
Kotlin

Parcelize:Parcelable 实现生成器

Parcelize:Parcelable 实现生成器Parcelable是安卓开发中常用的序列化方式,之前分享过一个Parcelable序列化插件,方便开发者快速生成代码,提高开发效率。1、Parcelize插件Kotlin官方也推出一个Parcelable自动生成的Gradle插件:Parcelize。1.1、引入插件在项目的build.gradle中添加:id("kotlin-parceliz

2.6k
Kotlin的继承
Kotlin

Kotlin的继承

Kotlin的继承1、超类:Any类似于Java中的Object类,Kotlin也有一个叫Any的超类,是所有类的父类,所有类都默认继承自该类。超类的定义如下,其中定义了三个函数:2、open注意上面的Any类用open修饰,表示该类可以被继承。Kotlin中的类默认都是final的,无法被继承:如果想让类可以被继承,就需要用在定义类的地方用open关键字标识该类可以被继承。3、构造器继承中涉及到

2.1k
 Kotlin基本语法
Kotlin

Kotlin基本语法

Kotlin基本语法了解Kotlin语言的基本语法。1、包kotlin文件以.kt结尾,编译文件还是如此只不过在文件名后面加上Kt后缀。这一点和Java不同,Java源文件.java,编译后为.class文件。1.1、包定义每个kotlin文件的第一行就用关键字package定义该文件所在的包。例:1.2、导包用到其它kotlin文件需要使用关键字import导入,这一点和Java一样。kotli

2.1k