Kotlin 五 泛型 枚举

一 泛型

泛型,即 "参数化类型",将类型参数化,能够用在类,接口,方法上。swift

与 Java 同样,Kotlin 也提供泛型,为类型安全提供保证,消除类型强转的烦恼。数组

声明一个泛型类:安全

class Box<T>(t: T) {
    var value = t
}

 建立类的实例时咱们须要指定类型参数: ide

 

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

 

定义泛型类型变量,能够完整地写明类型参数,若是编译器能够自动推定类型参数,也能够省略类型参数。code

Kotlin 泛型函数的声明与 Java 相同,类型参数要放在函数名的前面:对象

un <T> boxIn(value: T) = Box(value)

// 如下都是合法语句
val box4 = boxIn<Int>(1)
val box5 = boxIn(1)     // 编译器会进行类型推断

在调用泛型函数时,若是能够推断出类型参数,能够省略泛型参数。blog

如下实例建立了泛型函数 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 不是整型,也不是字符串")
    }
}

泛型约束

咱们可使用泛型约束来设定一个给定参数容许使用的类型。字符串

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)很是相似, 但能够安全使用

  

 

二 枚举类

枚举类最基本的用法是实现一个类型安全的枚举。

枚举常量用逗号分隔,每一个枚举常量都是一个对象。 

enum class Color{
    RED,BLACK,BLUE,GREEN,WHITE
}

枚举初始化

每个枚举都是枚举类的实例,它们能够被初始化:

enum class Color(val rgb: Int) {
    RED(0xFF0000),
    GREEN(0x00FF00),
    BLUE(0x0000FF)
}

默认名称为枚举字符名,值从0开始。若须要指定值,则可使用其构造函数:

enum class Shape(value:Int){
    ovel(100),
    rectangle(200)
}

 枚举还支持以声明本身的匿名类及相应的方法、以及覆盖基类的方法。如: 

enum class ProtocolState {
    WAITING {
        override fun signal() = TALKING
    },

    TALKING {
        override fun signal() = WAITING
    };

    abstract fun signal(): ProtocolState
}

  若是枚举类定义任何成员,要使用分号将成员定义中的枚举常量定义分隔开

 

使用枚举常量

Kotlin 中的枚举类具备合成方法,容许遍历定义的枚举常量,并经过其名称获取枚举常数。

EnumClass.valueOf(value: String): EnumClass  // 转换指定 name 为枚举值,若未匹配成功,会抛出IllegalArgumentException
EnumClass.values(): Array<EnumClass>        // 以数组的形式,返回枚举值

获取枚举相关信息:  

val name: String //获取枚举名称
val ordinal: Int //获取枚举值在全部枚举数组中定义的顺序

  

enum class Color{
    RED,BLACK,BLUE,GREEN,WHITE
}

fun main(args: Array<String>) {
    var color:Color=Color.BLUE

    println(Color.values())
    println(Color.valueOf("RED"))
    println(color.name)
    println(color.ordinal)

}

 自 Kotlin 1.1 起,可使用 enumValues<T>() 和 enumValueOf<T>() 函数以泛型的方式访问枚举类中的常量 : 

enum class RGB { RED, GREEN, BLUE }

inline fun <reified T : Enum<T>> printAllValues() {
    print(enumValues<T>().joinToString { it.name })
}

fun main(args: Array<String>) {
    printAllValues<RGB>() // 输出 RED, GREEN, BLUE
}
相关文章
相关标签/搜索