泛型,即 "参数化类型",将类型参数化,可以用在类,接口,方法上。
与 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")
}
网友评论