美文网首页
Kotlin范型摘要

Kotlin范型摘要

作者: 蒋扬海 | 来源:发表于2018-11-12 13:12 被阅读0次

范型类型参数

声明范型类型

interface List<T> { // List 接口定义了类型参数 T
    operator fun get(index: Int): T // 在接口或类的内部,T 可以当作普通类型使用
    // ...
}

你还可以用同样的语法声明泛型的扩展属性。

val <T> List<T>.penultimate: T  // 这个泛型扩展属性能在任何种类元素的列表上调用
    get() = this[size - 2]
>>> println(listOf(1, 2, 3, 4).penultimate) // 在这次调用中,类型参数 T 被推导成 Int
3

可以给类或接口的方法、顶层函数,以及扩展函数声明类型参数。

范型约束

上界约束

fun <T : Number> oneHalf(value: T): Double { // 指定 Number 为类型形参的上界
    return value.toDouble() / 2.0 // 调用 Number 类中的方法
}

制定多个上界(使用 where

fun <T> ensureTrailingPeriod(seq: T) where T : CharSequence, T : Appendable { // 类型参数约束的列表
    if (!seq.endsWith('.')) { // 调用为 CharSequence 接口定义的扩展函数
        seq.append('.') // 调用 Appendable 接口的方法
    }
}

事实上,没有指定上界的类型形参将会使用Any?这个默认的上界。

class Processor<T> {
    fun process(value: T) {
        value?.hashCode() // “value”是可空的,所以要用安全调用
    }
}

非空上界

class Processor<T : Any> { // 指定非“空”上界
    fun process(value: T) {
        value.hashCode() // 类型 T 的值现在是非“空”的
    }
}

类型擦除和实化类型参数

概念:

和 Java 一样,Kotlin 的泛型在运行时也被擦除了。这意味着泛型类实例不会携带用于创建它的类型实参的信息。例如,如果你创建了一个List<String>并将一堆字符串放到里面,在运行时你只能看到它是一个List。不能识别出列表本打算包含的是哪种类型的元素。

在调用泛型函数的时候,在函数体中你不能决定调用它用的类型实参:

>>> fun <T> isA(value: Any) = value is T
Error: Cannot check for instance of erased type: T

通常情况下都是这样,只有一种例外可以避免这种限制:内联函数。

如果你把前面例子中的isA函数声明成inline并且用reified标记类型参数,你就能够用该函数检查value是不是T的实例。

inline fun <reified T> isA(value: Any) = value is T // 现在代码可以编译了

>>> println(isA<String>("abc"))
true
>>> println(isA<String>(123))
false

另一个例子

val serviceImpl = ServiceLoader.load(Service::class.java)

现在让我们用带实化类型参数的函数重写这个例子:

val serviceImpl = loadService<Service>()

我们看看这个loadService函数是如何定义的:

inline fun <reified T> loadService() { // 类型参数标记成了“reified”
    return ServiceLoader.load(T::class.java) // 把 T::class 当成类型形参的类访问
}

Android 中的使用

inline fun <reified T : Activity>
        Context.startActivity() { // 类型参数标记成了“reified”
    val intent = Intent(this, T::class.java) // 把 T::class 当成类型参数的类访问
    startActivity(intent)
}

startActivity<DetailActivity>() // 调用方法显示 Activity

变型:协变与逆变

Java 中要声明一个协变的泛型类型,你只需要:

`List<Dog> dogs = new ArrayList<>();List<? extends Animal> animals = dogs;`

extends关键字将动物列表与狗狗列表之间的关系声明为了协变

需要逆变的时候,只需:

`Compare<Animal> animalCompare = (first, second) -> first.getSize() - second.getSize();Compare<? super Spider> spiderCompare = animalCompare;`

super关键字将动物比较器与蜘蛛比较器之间的关系声明为了逆变

Kotlin 中,要声明类在某个类型参数上是可以协变的,在该类型参数的名称前加上out关键字就可以:

interface Producer<out T> { // 类被声明成在 T 上协变。
    fun produce(): T
}

而逆变则是通过 in 关键字:

interface Comparator<in T>  {
    fun compare(e1: T, e2: T): Int { ... } // 在“in”位置使用 T。
}

类的类型参数前的out关键字要求所有使用T的方法只能把T放在out位置而不能放在in位置。

一个类可以在一个类型参数上协变同时在另外一个类型参数上逆变。

interface Function1<in P, out R> {
    operator fun invoke(p: P): R
}

型变是比较重要但不是像 1 + 1 = 2 这种概念这么容易理解,可以在具体使用过程中细细体会。

星号投影
  • kt 中 <*> 等价于 java 中的 <?>
  • 星号投影不能写入,只能读取
  • 与 Any? 是不同的: List<*> 中的元素只能是一种数据类型,但 List<Any?> 的元素可以是任意类型。

相关文章

  • Kotlin范型摘要

    范型类型参数 声明范型类型 你还可以用同样的语法声明泛型的扩展属性。 可以给类或接口的方法、顶层函数,以及扩展函数...

  • 范型--Java与kotlin

    缘起(为解决什么问题而生) 加强类型安全 减少类型强转次数 限定参数或返回值范围 类型安全 类型赋值检查 类型调用...

  • 4.数据类型(data_Type)

    整数型 kotlin_整型 swift_整型 浮点型 kotlin_浮点型 swift_浮点型 布尔型 kotli...

  • Kotlin---标准扩展函数

    介绍 除了自定义扩展之外,Kotlin中也定义了很多的扩展函数,而这些扩展函数的接收类型是范型,也就是所有对象都可...

  • 泛型

    与Java泛型相同,Kotlin同样提供了泛型支持。对于简单的泛型类、泛型函数的定义,Kotlin 与 Java ...

  • Kotlin---泛型

    Kotlin不变型泛型 Kotlin的不变型泛型和Java一样,通过声明泛型类型来使用泛型类。而该种泛型声明后,则...

  • CoreJava笔记 - 范型程序设计(2)

    范型代码和Java虚拟机 关键知识:类型擦除Java的范型是编译器层次的范型,而在Java虚拟机上并没有范型类。在...

  • Kotlin 泛型 VS Java 泛型

    建议先阅读我的上一篇文章 -- Java 泛型 和 Java 泛型一样,Kotlin 泛型也是 Kotlin 语言...

  • Kotlin 范型之协变、逆变

    一. 类(Class) 与类型(Type) Kotlin 中类和类型是不一样的概念。 下图充分展示了它们的区别。 ...

  • Kotlin for android学习六:泛型

    前言 kotlin官网和kotlin教程学习教程的笔记。 1. 声明泛型 2. 泛型约束 : 对泛型的类型上限进行...

网友评论

      本文标题:Kotlin范型摘要

      本文链接:https://www.haomeiwen.com/subject/xcaqfqtx.html