IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Kotlin的泛型:泛型类型参数 -> 正文阅读

[移动开发]Kotlin的泛型:泛型类型参数

Kotlin 的泛型是指带类型形参的类型。当泛型的实例被创建时,类型形参被替换为具体类型。
?

以 Kotlin 的 list 方法为例,当创建一个字符串列表时,类型形参 T 被替换为具体类型 String。

val strList: List<String>
public inline fun <T> List(size: Int, init: (index: Int) -> T): List<T> = MutableList(size, init)

除了 List,还可以给一个类型声明多个类型形参,比如 Map 有 Key 和 Value 两个类型形参。

val map : Map<String, Person>
public interface Map<K, out V>

和 Kotlin 的普通类型一样,泛型也支持类型推导。

val authors = listOf("Dmitry", "Svetlana")

authors 没有显示指明它是字符串列表,但是因为 list 里面有两个字符串,所以被推导为字符串列表类型。
?

但是如果是一个空列表,就必须指定类型参数。因为编译器也不知道列表里面是什么类型的值,无法推导泛型的类型参数。

    val readers: List<String> = mutableListOf()

也可以在 mutableListOf 指定类型实参。

    val readers = mutableListOf<String>()

Kotlin 的泛型类型实参和 Java 的类型实参有一些区别。

Kotlin 必须显式指定类型实参或者可以被推导出类型实参。而 Java 可以不指定类型实参。
?

Java 可以这么写:

List list = new ArrayList();

但是 Kotlin 不行。这是因为 Java 的泛型是 Java 1.5 版本引入的特性,为了兼容老版本,它必须可以写成不带类型实参的形式。而 Kotlin 从一开始就设计了泛型,必须定义类型实参。

泛型函数和属性

泛型函数

大部分使用集合的库函数都是泛型的。比如 slice。

public fun <T> List<T>.slice(indices: IntRange): List<T> {
    if (indices.isEmpty()) return listOf()
    return this.subList(indices.start, indices.endInclusive + 1).toList()
}

fun 后面的 表示类型形参声明。
?

List 表示接收者是 List 类型,一个由 T 类型组成的列表。slice 函数是这个 List 类型的成员函数或者扩展函数。
?

: List 表示 slice 的返回值也是一个由 T 类型组成的列表。
?

调用泛型函数可以显式指定类型实参,也可以完全不写类型实参,由编译器推导类型。

    val letters = ('a'..'z').toList()
    // 输出[a, b, c]
    println(letters.slice<Char>(0..2))
	// 输出[k, l, m, n]
    println(letters.slice(10..13))

泛化的高阶函数

泛化的高阶函数是指函数参数为泛型函数的高阶函数。
?

filter 方法接收一个 (T) -> Boolean 类型的 predicate 函数,它的类型形参和接收者的类型形参相同。

public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
    return filterTo(ArrayList<T>(), predicate)
}

?

调用 List 的 filter 方法。编译器推导出 it 的类型是 String,和接收者的类型参数相同。

private fun filter() {
    val authors = listOf("Dmitry", "Svetlana")
    val readers: List<String> = mutableListOf("Dmitry", "Svetlana", "zhangsan", "lisi")
    // 输出[zhangsan, lisi]
    readers.filter {
        it !in authors
    }.apply { println(this) }
}

泛型属性

和泛型函数一样,还可以用相同的语法声明泛型的扩展属性。
?

比如返回列表的倒数第二个元素。

val <T> List<T>.penultimate : T
    get() = this[size - 2]

?

不能声明泛型非扩展属性。因为普通属性不能存储多个不同类型的值,只有带接收者定义的扩展属性才有意义。
?

以下代码编译器会报错,属性的类型参数只能用在它的接收者类型。也就是扩展属性。

val <T> x: T = 1

声明泛型类

泛型类使用 <> 操作符定义类型参数。
?

List 接口定义了 T 类型的类型参数,T 类型可以作为 get 方法的返回值类型。

interface List<T> {
    operator fun get(index: Int): T
}

继承泛型类

继承泛型类有 2 种方法:

  1. 指定具体类型
  2. 继续使用类型参数

指定具体类型

class StringList : List<String> {
    override fun get(index: Int): String {
        TODO("Not yet implemented")
    }
}

继续使用类型参数

class ArrayList<T> : List<T> {
    override fun get(index: Int): T {
        TODO("Not yet implemented")
    }
}

继承类自身也可以是泛型的类型参数。
?

比如 String 类继承了 Comparable 接口,同时它也是 Comparable 的类型参数 。

interface Comparable<T> {
    fun compareTo(other: T): Int
}

class String : Comparable<String> {
    override fun compareTo(other: String): Int {
        TODO("Not yet implemented")
    }
}

类型参数约束

类型参数约束可以限制泛型的参数类型。比如对 List 的所有元素求和,可以对 List 或者 List 求和,但是无法对 List 求和。所以求和函数必须限制 List 的类型参数。

类型参数的上界

Kotlin 使用 : 限定类型参数的上界。与 Java 的 extend 类似。

fun <T : Number> List<T>.sum(): T

sum 方法的类型参数必需是 Number 的子类。
?

当类型参数的上界被指定后,可以调用上界的方法。
?

比如 oneHalf 调用了 Number 的 toDouble。

fun <T : Number> oneHalf(num: T): Double {
    return num.toDouble() / 2.0
}

fun main() {
    // 1.5
    println(oneHalf(3))
}

max 函数比较了两个值,因此它的类型参数必须实现了 Comparable 接口,上界为 Comparable。
?

String 类型实现了 Comparable,因此可以比较 “kotlin”, “java”。

fun <T : Comparable<T>> max(first: T, second: T): T {
    return if (first > second) {
        first
    } else {
        second
    }
}

fun main() {
    // kotlin
    println(max("kotlin", "java"))
}

类型参数的多重约束

泛型的类型参数可以设置多个约束。
?

比如类型参数 T 既继承了 CharSequence,也继承了 Appendable。这样它可以同时调用 endsWith 和 append。
endsWith 是 CharSequence 的方法,append 是 Appendable 的方法。

fun <T> ensureTrailingPeriod(seq: T) where T : CharSequence, T : Appendable {
    if (!seq.endsWith('.')) {
        seq.append('.')
    }
}

fun main() {
    val helloWorld = StringBuilder("Hello World")
    ensureTrailingPeriod(helloWorld)
    // Hello World.
    println(helloWorld)
}

让类型参数非空

泛型的类型参数可以为空类型。有时候需要约束泛型类型不为空,保证参数为非空类型。
?

在 process 内部使用 ?. 安全调用,保证 arg 为空时打印 “null arg”。

class Processor<T> {
    fun process(arg : T) {
        arg?.hashCode() ?: println("null arg")
    }
}

fun main() {
    val nullableProcessor = Processor<String?>()
    // null arg
    nullableProcessor.process(null)
}

更好的方法是指定类型参数的上界为 Any 类型。Any 类型是所有非空类型的父类。

class Processor<T : Any> {
    fun process(arg: T) {
        arg.hashCode()
    }
}

fun main() {
    // 编译错误:Type argument is not within its bounds.
    val nullableProcessor = Processor<String?>()
    nullableProcessor.process(null)
}

Processor 这里也可以不用 Any,只要是任何非空类型作为上界就行。
?

题外话:这里的示例直接调用 hashCode 也不会报错。因为 hashCode 也是一个扩展方法。

class Processor<T> {
    fun process(arg: T) {
        // 不会报错
        arg.hashCode()
    }
}

fun main() {
    val nullableProcessor = Processor<String?>()
    nullableProcessor.process(null)
}

hashCode 是 Any? 的扩展方法,如果接收者为空,直接返回 0。
?

HashCode.kt

package kotlin

import kotlin.internal.InlineOnly


/**
 * Returns a hash code value for the object or zero if the object is `null`.
 *
 * @see Any.hashCode
 */
@SinceKotlin("1.3")
@InlineOnly
public inline fun Any?.hashCode(): Int = this?.hashCode() ?: 0
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-02-16 13:14:01  更:2022-02-16 13:15:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 14:21:53-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码