Kotlin 范型之泛型约束、类型投影、星号投影

简介: Kotlin 范型之泛型约束、类型投影、星号投影

一. 泛型约束(Generic constraints)



Kotlin 跟 Java 一样,也拥有泛型约束。Java 使用 extends 关键字指明上界。


在 Kotlin 中使用:代替 extends 对泛型的的类型上界进行约束。


1.1 Upper bounds


下面的代码,在调用 sum() 函数时,传入的参数只能是 Number 及其子类,如果是其他类型,则会报错。

fun <T : Number> sum(vararg param: T) = param.sumByDouble { it.toDouble() }
fun main() {
    val result1 = sum(1,10,0.6)
    val result2 = sum(1,10,0.6,"kotlin") // compile error
}


Kotlin 默认的上界是Any?,为何是Any?而不是 Any 呢?


Any 类似于 Java 中的 Object,它是所有非空类型的超类型。但是 Any 不能保存 null 值,如果需要 null 作为变量的一部分,则需要使用Any?Any?是 Any 的超类型,所以 Kotlin 默认的上界是Any?


1.2 where 关键字


当一个类型参数指定多个约束时,在 Java 中使用&连接多个类、接口。

class ClassA { }
interface InterfaceB { }
public class MyClass<T extends ClassA & InterfaceB> {
    Class<T> variable;
}


而在 Kotlin 中,使用 where 关键字实现这个功能。下面的代码,T 必须继承 ClassA 以及实现 InterfaceB。

open class ClassA
interface InterfaceB
class MyClass<T>(var variable: Class<T>) where T : ClassA, T : InterfaceB


二. 类型投影(Type projections)



在上一篇文章<<Kotlin 范型之协变、逆变>>中,曾经介绍过 MutableList 是不变的,可读可写,没有使用 in、out 修饰。


如果对 MutableList 的参数类型使用 in 或者 out 修饰,会发生什么情况呢?


下面的代码说明了一切:

fun main() {
    val list1:MutableList<String> = mutableListOf()
    list1.add("hello")
    list1.add("world")
    val list2:MutableList<out String> = mutableListOf()
    list2.add("hello")  // compile error
    list2.add("world")  // compile error
    val list3:MutableList<in String> = mutableListOf()
    list3.add("hello")
    list3.add("world")
    lateinit var list4:MutableList<String>
    list4 = list3;     // compile error
}


使用 out 时,会报错,因为该参数只能出现在方法的返回类型。而使用 in 时,编译可以通过,因为该参数只能出现在方法的入参。


此时,list2 和 list3 分别表示一个受限制的 MutableList。在 Kotlin 中,这种行为被称之为类型投影。其主要作用是参数作限定,避免不安全操作。


正是由于 list3 是一个受限制的 MutableList,因此它赋值给 list4 报错也是可以理解了。


三.星号投影(Star-projections)



星号投影用来表明“不知道关于泛型实参的任何信息”。


类似于 Java 中的无界类型通配符?, Kotlin 使用星号投影*


*代指了所有类型,相当于Any?


例如:MutableList<*> 表示的是 MutableList<out Any?>

fun main() {
    val list1 = mutableListOf<String>()
    list1.add("string1")
    list1.add("string2")
    printList(list1)
    val list2 = mutableListOf<Int>()
    list2.add(123)
    list2.add(456)
    printList(list2)
}
fun printList(list: MutableList<*>) {
    println(list[0])
}


正是由于使用 out 修饰以及星号投影的类型不确定性,会导致写入的任何值都有可能跟原有的类型冲突。因此,星号投影不能写入,只能读取。


四.总结



本文是 Kotlin 范型系列的最后一篇文章。


本文讲述了 Kotlin 泛型约束、类型投影、星号投影的特性。范型是 Kotlin 的高级特性,相对于 Java 的范型,它拥有更多的概念。

相关文章
|
3天前
|
安全 Java Kotlin
Kotlin泛型:灵活的类型参数化
Kotlin泛型:灵活的类型参数化
6 1
|
4天前
|
Kotlin
Kotlin中的数值类型
Kotlin中的数值类型
10 2
|
4天前
|
存储 安全 Kotlin
Kotlin中的可空类型
Kotlin中的可空类型
10 1
|
4天前
|
存储 Kotlin
Kotlin中布尔类型、字符类型、字符串类型和数组类型
Kotlin中布尔类型、字符类型、字符串类型和数组类型
7 1
|
12天前
|
安全 Java 编译器
Android面试题之Java 泛型和Kotlin泛型
**Java泛型是JDK5引入的特性,用于编译时类型检查和安全。泛型擦除会在运行时移除类型参数,用Object或边界类型替换。这导致几个限制:不能直接创建泛型实例,不能使用instanceof,泛型数组与协变冲突,以及在静态上下文中的限制。通配符如<?>用于增强灵活性,<? extends T>只读,<? super T>只写。面试题涉及泛型原理和擦除机制。
18 3
Android面试题之Java 泛型和Kotlin泛型
|
8月前
|
编译器 C++ Kotlin
【Kotlin】基础速览(1):操作符 | 内建类型 | 类型转换 | 字符串模板 | 可变 var 和不可变 val
【Kotlin】基础速览(1):操作符 | 内建类型 | 类型转换 | 字符串模板 | 可变 var 和不可变 val
42 0
|
11月前
|
Kotlin
Kotlin中接口、抽象类、泛型、out(协变)、in(逆变)、reified关键字的详解
Kotlin中接口、抽象类、泛型、out(协变)、in(逆变)、reified关键字的详解
70 0
|
11月前
|
Java 编译器 Kotlin
Kotlin 中变量,类型,表达式,函数详解
Kotlin 中变量,类型,表达式,函数详解
73 0
|
10月前
|
安全 Java 编译器
Kotlin 泛型 VS Java 泛型
Kotlin 泛型 VS Java 泛型
48 0
|
11月前
|
Java Kotlin
Kotlin中与Java互操作与可空性、类型映射、属性访问、@JvmOverloads、@JvmField、@JvmStatic、@Throws和函数类型操作详解
Kotlin中与Java互操作与可空性、类型映射、属性访问、@JvmOverloads、@JvmField、@JvmStatic、@Throws和函数类型操作详解
73 0