泛型,即 "参数化类型",将类型参数化,可以用在类,接口,方法上。
与 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>("W3cschool")
println(boxInt.value)
println(boxString.value)
}
输出结果为:
10
W3cschool
定义泛型类型变量,可以完整地写明类型参数,如果编译器可以自动推定类型参数,也可以省略类型参数。
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 = ""
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
字符串转换为大写:W3CSCHOOL
T 不是整型,也不是字符串
我们可以使用泛型约束来设定一个给定参数允许使用的类型。
Kotlin 中使用 : 对泛型的类型上限进行约束。
最常见的约束是上界(upper bound):
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>> 的子类型
默认的上界是 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 中没有通配符类型,它有两个其他的东西:声明处型变(declaration-site variance)与类型投影(type projections)。
声明处的类型变异使用协变注解修饰符:in、out,消费者 in, 生产者 out。
使用 out 使得一个类型参数协变,协变类型参数只能用作输出,可以作为返回值类型但是无法作为入参的类型:
// 定义一个支持协变的类
class W3cschool<out A>(val a: A) {
fun foo(): A {
return a
}
}
fun main(args: Array<String>) {
var strCo: W3cschool<String> = W3cschool("a")
var anyCo: W3cschool<Any> = W3cschool<Any>("b")
anyCo = strCo
println(anyCo.foo()) // 输出 a
}
in 使得一个类型参数逆变,逆变类型参数只能用作输入,可以作为入参的类型但是无法作为返回值的类型:
// 定义一个支持逆变的类
class W3cschool<in A>(a: A) {
fun foo(a: A) {
}
}
fun main(args: Array<String>) {
var strDCo = W3cschool("a")
var anyDCo = W3cschool<Any>("b")
strDCo = anyDCo
}
有些时候, 你可能想表示你并不知道类型参数的任何信息, 但是仍然希望能够安全地使用它. 这里所谓"安全地使用"是指, 对泛型类型定义一个类型投射, 要求这个泛型类型的所有的实体实例, 都是这个投射的子类型。
对于这个问题, Kotlin 提供了一种语法, 称为 星号投射(star-projection):
如果一个泛型类型中存在多个类型参数, 那么每个类型参数都可以单独的投射. 比如, 如果类型定义为interface Function<in T, out U> , 那么可以出现以下几种星号投射:
注意: 星号投射与 Java 的原生类型(raw type)非常类似, 但可以安全使用
类型转换用于将一种数据类型的变量转换为另外一种类型的变量。Go 语言类型转换基本格式如下: type_name(expression) type_name ...
介绍商品卡片,用于展示商品的图片、价格等信息。引入通过以下方式来全局注册组件,更多注册方式请参考组件注册。import { creat...
介绍用于在内容加载过程中展示一组占位图形。实例演示引入通过以下方式来全局注册组件,更多注册方式请参考组件注册。import { c...
引导用户按照流程完成任务的分步导航条,可根据实际应用场景设定步骤,步骤不得少于 2 步。基础用法简单的步骤条。设置active属...
Timeline 时间线可视化地呈现时间流信息。基础用法Timeline 可拆分成多个按照时间戳排列的 activity,时间戳是其区分于其他控件...
表格,在Bootstrap4中经常被使用到。那么在Bootstrap4中,如何设置各种样式的表格呢?基础表格在Bootstrap4中,我们可以使用.tab...
卡片是一个灵活的、可扩展的内容窗口。它包含了可选的卡片头和卡片脚、一个大范围的内容、上下文背景色以及强大的显示选项。创建...
弹出框控件和提示框有点类似,用户用鼠标点击元素后,就会显示。不过和提示框不同的是,弹出框能够显示的内容更多。创建弹出框通...