Kotlin速记-泛型

Kotlin 泛型

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

声明一个泛型类

// 声明一个泛型
class Test<T>(t: T) {
    var value = t
}// 创建类的实例时我们需要指定类型参数
val test: Test<Int> = Test<Int>(1)
​
test
res28: Line_26.Test<kotlin.Int> = Line_26$Test@206b47bc
​
val test = Test(1) 
test
res31: Line_26.Test<kotlin.Int> = Line_26$Test@6fc93ae7
​
​
// 传递数据
val ageTest = Test<Int>(18)
ageTest.value
res34: kotlin.Int = 18val nameTest = Test<String>("Simplation")
nameTest.value
res36: kotlin.String = Simplation
​
​
// 定义泛型类型变量,可以完整地写明类型参数,如果编译器可以自动推定类型参数,也可以省略类型参数。Kotlin 泛型函数的声明与 Java 相同,类型参数要放在函数名的前面
fun <T> test(value: T) = Test(value)// 以下都是合法语句
val t1 = test<Int>(1)
val t2 = test(2)
​
t1.value
res39: kotlin.Int = 1
​
t2.value
res40: kotlin.Int = 2// 在调用泛型函数时,如果可以推断出类型参数,可以省略泛型参数。以下实例创建了泛型函数 doPrintln,函数根据传入的不同类型做相应处理
fun <T> doPrintln(content: T) {when (content) {
        is Int -> println("整型数字为 $content")
        is String -> println("字符串转换为大写:${content.toUpperCase()}")
        else -> println("T 不是整型,也不是字符串")
    }
}val age = 18
val name = "Simplation"
val bool = true// 整型
doPrintln(age)    
整型数字为 18// 字符串
doPrintln(name)   
字符串转换为大写:SIMPLATION
​
// 布尔型
doPrintln(bool)  
T 不是整型,也不是字符串

泛型约束

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

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

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

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

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

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

默认的上界是 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() }
}

星号投射

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

对于这个问题, 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<in T, out U> , 那么可以出现以下几种星号投射:

  • Function<*, String> , 代表 Function<in Nothing, String> ;
  • Function<Int, *> , 代表 Function<Int, out Any?> ;
  • Function<, > , 代表 Function<in Nothing, out Any?> .

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

// 星号投射,是 * 代指了所有类型,相当于 Any?class A<T>(val t: T, val t2 : T, val t3 : T)
class Apple(var name : String)//使用类    
val a1: A<*> = A(12, "String", Apple("苹果"))
val a2: A<Any?> = 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)
}

型变

Kotlin 中没有通配符类型,它有:声明处型变(declaration-site variance)与类型投影(type projections)
声明处型变
声明处的类型变异使用协变注解修饰符:in、out,消费者 in, 生产者 out。

  • 使用 out 使得一个类型参数协变,协变类型参数只能用作输出,可以作为返回值类型但是无法作为入参的类型:
// 定义一个支持协变的类
class Test<out A>(val a: A) {
 fun foo(): A {
 return a
    }
}var strCo: Test<String> = Test("a")
var anyCo: Test<Any> = Test<Any>("b")
anyCo = strCo
println(anyCo.foo())   // 输出 a
  • in 使得一个类型参数逆变,逆变类型参数只能用作输入,可以作为入参的类型但是无法作为返回值的类型
// 定义一个支持逆变的类
class Test<in A>(a: A) {
 fun foo(a: A) {
    }
}var strDCo = Test("a")
var anyDCo = Test<Any>("b")
strDCo = anyDCo
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值