泛型,即 "参数化类型",将类型参数化,可以用在类,接口,方法上。

与 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 不是整型,也不是字符串

泛型约束

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

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 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
}

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

// 定义一个支持逆变的类
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
}

星号投射

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

对于这个问题, Kotlin 提供了一种语法, 称为 星号投射(star-projection):

  • 假如类型定义为 Foo<out T> , 其中 T 是一个协变的类型参数, 上界(upper bound)为 TUpper ,Foo<> 等价于 Foo<out TUpper> . 它表示, 当 T 未知时, 你可以安全地从 Foo<> 中 读取TUpper 类型的值.
  • 假如类型定义为 Foo<in T> , 其中 T 是一个反向协变的类型参数, Foo<> 等价于 Foo<inNothing> . 它表示, 当 T 未知时, 你不能安全地向 Foo<> 写入 任何东西.
  • 假如类型定义为 Foo<T> , 其中 T 是一个协变的类型参数, 上界(upper bound)为 TUpper , 对于读取值的场合, Foo<*> 等价于 Foo<out TUpper> , 对于写入值的场合, 等价于 Foo<in Nothing> .

如果一个泛型类型中存在多个类型参数, 那么每个类型参数都可以单独的投射. 比如, 如果类型定义为interface Function<in T, out U> , 那么可以出现以下几种星号投射:

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

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

Kotlin 泛型的更多相关文章

  1. Kotlin泛型与协变及逆变原理剖析

    在上一次https://www.cnblogs.com/webor2006/p/11234941.html中学习了数据类[data class]相关的知识,这次会学习关于泛型相关的东东,其中有关于泛型 ...

  2. kotlin 泛型约束

    fun <T:Comparable<T>> sort(list :List<T>){} 冒号之后指定的类型就是泛型参数的上界,对于泛型参数T,只允许使用Compar ...

  3. kotlin 泛型函数

    fun<T> singletonList(item:T):List<T>{ ..... } fun<T>T.basicToString():String{ .... ...

  4. kotlin泛型中星号投射

    如果一个泛型类型中存在多个类型的参数,那么每个类型的参数都可以单独投射,例如:如果类型定义为:"interface Function<in T,out>",那么可以出现 ...

  5. kotlin 泛型中类型投射

    fun main(arg: Array<String>) { var ints:Array<Int> = arrayOf(, , ) val any =Array<Any ...

  6. kotlin泛型基本使用

    class box<T> (t :T){ var vlaue =t } fun main(arg: Array<String>) { val box1:box<Int&g ...

  7. Kotlin星投影与泛型约束详解

    星投影(star projection): 继续来学习Kotlin泛型相关的东东,星投影(star projection),这是个啥东东呢?下面先来说一下概念: 1.对于Star<out T&g ...

  8. C#泛型的抗变与协变

    C#泛型的抗变与协变 学习自 C#本质论6.0 https://www.cnblogs.com/pugang/archive/2011/11/09/2242380.html Overview 一直以来 ...

  9. Kotlin in Action 笔记

    Kotlin 参考 官网 reference kotlin实战 Try Kotlin Kotlin China Github 简介 Kotlin是一门把Java平台作为目标的新的编程语言.它简洁.安全 ...

随机推荐

  1. Cocos Creator cc.Button (脚本事件内容)

    cc.Class({extends: cc.Component,properties: {}, onLoad: function () { var clickEventHandler = new cc ...

  2. Backup &recovery备份和还原

    实践版本:MySQL5.7 备份类型(backup type)物理和逻辑备份(Physical Versus Logical Backup)        物理备份是指直接复制存储数据库内容的目录和文 ...

  3. MQ消息队列配置

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  4. python数据类型之列表类型

    一.列表 作用:多个装备,多个爱好,多门课程,多个女朋友等 定义:[]内可以有多个任意类型的值,逗号分隔 以下是列表的常用操作方法: append(), count(), insert(),rever ...

  5. C语言 全局变量、静态全局变量、局部变量、静态局部变量

    //test.c #include <stdio.h> extern int global_var; void test_global_var() { global_var++; prin ...

  6. IO流(二)

    二.File类 概述 文件和目录路径名的抽象表示形式 构造方法 public File(String pathname) public File(String parent,String child) ...

  7. Machine Learning 第三周

    ML week3 逻辑回归 Logistic Function h_\theta(x)=g(\theta^Tx) g(t)=\frac{1}{1+e^{-z}} 当t大于0, 即下面公式成立时,y=1 ...

  8. 第01节:ActiveMQ入门和消息中间件

    1.ActiveMQ最主要的功能:实现JMS Provider,用来帮助实现高可用.高性能.可伸缩.易用和安全的企业级面向消息服务的系统.是一个异步的功能. 2.ActiveMQ特点: 完全支持JMS ...

  9. TeamCity 创建docker构建步骤

    1 dockerfile source 选择dockerfile文件的路径,一共有三种方式: 1.1.1 file content 这种方式是在线写dockerfile文件. 其在进行创建的时候会在 ...

  10. postman的几个问题

    最近使用postman写了几个web接口测试用例,工具使用比较简单,大概步骤如下: 1.new collections——>建文件夹,类似建一个测试合集,用于方便整理,例如可以把同一个接口各种参 ...