Kotlin之泛型

Android  Kotlin  2024年10月11日 am8:22发布1周前更新 91es.com站长
27 0 0

前言

简单记录一下泛型的使用,记录于此,方便自己查阅。

正文

什么是泛型

泛型即“参数化类型”,就是将具体的类型变成参数化类型,在声明一个泛型时,传递的是一个类型形参,在调用时传递的是一个类型实参。

当定义泛型时,泛型是在类型名之后、主构造函数之前用尖括号“<>”括起来的大写字母类型参数。当定义泛型类型变量时,可以完整地写明类型参数,如果编译器可以自动推断类型参数,则可以省略类型参数。

class ArrayList<E>

上述代码中E表示某种类型,定义时是一个未知类型,称为类型形参。E这个类型类似一个参数,当创建ArrayList实例时,需要传入具体的类型。

示例代码如下:

var list1 = arrayListOf<String>("aa","bb","cc") 
var list2 = arrayListOf<Long>(111,222,333) 
var list3 = arrayListOf<Int>(1,2)

上面传入的参数String、Long、Int都是类型实参。

泛型分类

泛型分为3种类型,分别是泛型类、泛型接口以及泛型方法

泛型类

使用泛型标记的类,被称为泛型类。泛型类的使用分为两种情况,一种是泛型类被用于实例化,另一种是泛型类被用于继承。

当泛型类用于实例化时,需要传递具体的类型实参。

泛型的符号一般使用大写字母,如<T>、<E>、<K>、<V>等,泛型符号可以是满足Kotlin命名规则的任意字符,甚至可以是某一个单词,如<TYPE>,一个类可以声明多个泛型,只要使用不同的泛型符号即可

  1. 泛型类被用于实例化

val list = ArrayList<String>()     //String类型为泛型实参 
val map = HashMap<String, Int>()  //String、Int 为泛型实参 
val set = HashSet<Long>()          //Long 为泛型实参  
  1. 泛型类被用于继承

当泛型类被用于继承时,需要为泛型形参提供一个具体类型或者另一个类型的形参。

open abstract class Parent<T> {
    abstract fun get(): T;
}
//1. 继续泛型(这样可以传入Int,Long,Double等)
class Son<T> : Parent<T>() {
    override fun get(): T {
        TODO("Not yet implemented")
    }
}
//2. 具体的一个类型
class Son2<Int> : Parent<Int>() {
    override fun get(): Int {
        TODO("Not yet implemented")
    }
}
泛型接口

使用泛型标记的接口,被称为泛型接口。泛型接口的使用分为两种情况。

  1. 情况一

泛型接口被实现时已经确定泛型接口对应的实参,直接传递实参。

interface List<String> : Collection<String>{} 
  1. 情况二

泛型接口被实现时不能够确定泛型接口对应的实参,则需要使用当前类或者接口的泛型形参

interface List<E> : Collection<E>{}
泛型方法

使用泛型标记的方法,被称为泛型方法。

泛型方法在被调用时,只需传入具体的泛型实参即可。

//String类型为泛型实参,Kotlin自动推断 
val list = arrayListOf("a","b","c")  
//String、Int 为泛型实参,Kotlin自动推断 
val map = hashMapOf("a" to 1,"b" to 2)  
//Long 为泛型实参,Kotlin自动推断 
val set = hashSetOf(1L,2L,3L)  

当然,也可以自己定义泛型方法。

fun <T> printType(a: T) {
    when (a) {
        is Int -> {
            "Int 类型"
        }
        is String -> {
            "String 类型"
        }
        is Double -> {
            "Double 类型"
        }
    }
}

泛型约束

泛型约束是对类或者方法中的类型变量进行约束。

当创建一个泛型List<E>时,类型变量E理论上是可以被替换为任意的引用类型,但是有时候需要约束泛型实参的类型,例如想对E类型变量求和,则E应该是Int类型、Long类型、Double类型或者Float类型等,而不应该是String类型,因此在特殊情况下,需要对类型变量E进行限制。

泛型约束格式:

<T:Type>

其中Type可以称为绑定类型,绑定类型可以是类或者接口。

  1. 如果绑定类型是一个类,则类型参数T必须是Type的子类。

  2. 如果绑定类型是一个接口,则类型参数T必须是Type接口的实现类。

调用泛型上界类中的方法

上界是Number类型的。Double,Int等都是继承Number。如果传入其他类型,会提示不匹配。

  1. 一个约束条件

fun <T : Number> add(a: T, b: T): Double {
    return a.toDouble() + b.toDouble();
}
add(100, 100)
add(10f, 100f)
//类型不匹配[错误]
add("a", "b")
  1. 多个约束条件

如果上界约束需要多个约束,则可以通过where语句来完成。

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

通过where关键字实现了上界约束的多个约束,每个约束中间用逗号分隔,并且传递的参数value可以调用第1个约束CharSequence类中的endsWith()方法,同时也可以调用第2个约束Appendable类中的append()方法。

泛型约束<T:Any?>与<T:Any>

在泛型<T:类或者接口>中,有两种特别的形式,分别是<T:Any?>和<T:Any>。

  1. <T:Any?>表示类型实参是Any的子类,且类型实参可以为null。

  2. <T:Any>表示类型实参是Any的子类,且类型实参不可以为null。

// 声明<T : Any?>等同于<T>
fun <T : Any?> nullAbleProcessor(value: T) {
    value?.hashCode()
}
fun <T : Any> nullDisableProcessor(value: T) {
    value.hashCode()
}

使用情况

//编译成功
nullAbleProcessor(null)

<T:Any?>表示可以接收任意类型的类型参数,这个任意类型中包含null。

//无法编译,提示[Null can not be a value of a non-null type TypeVariable(T)]
nullDisableProcessor(null)

<T:Any>表示可以接收任意类型的类型参数,这个任意类型中不包含null。

如果要可传输null,需要改为

fun <T : Any> nullDisableProcessor(value: T?) {
    value?.hashCode()
}

参考文章

  1. 《Kotlin从基础到实战》

 历史上的今天

  1. 2022: Android中onConfigurationChanged的总结(0条评论)
  2. 2020: GestureDetector使用简介(0条评论)
  3. 2019: 季羡林:论坏人(0条评论)
版权声明 1、 本站名称: 91易搜
2、 本站网址: 91es.com3xcn.com
3、 本站内容: 部分来源于网络,仅供学习和参考,若侵权请留言
3、 本站申明: 个人流水账日记,内容并不保证有效

暂无评论

暂无评论...

随机推荐

[代码片段]SoundPool的使用

前言之前也整理过SoundPool的使用,但由于没有附上代码,这里就重新整理一下。好记性不如烂笔头正文参数和使用方式就不细写了,简单说一下SoundPool.play()方法public final int play(int soundID, // soundID通过...

修改ListVieW等滚动条图片

前言本文简单的介绍Java代码中修改RecyclerView、ListView和GridView普通滚动条图片。正文隐藏内容!付费阅读后才能查看!¥2 ¥3多个隐藏块只需支付一次付费阅读参考文件

王小波:一只特立独行的猪

插队的时候,我喂过猪、也放过牛。假如没有人来管,这两种动物也完全知道该怎样生活。它们会自由自在地闲逛,饥则食渴则饮,春天来临时还要谈谈爱情;这样一来,它们的生活层次很低,完全乏善可陈。人来了以后,给它们的生活做出了安排:每一头牛和每一口猪的生活都有了主题。就它们中的大多数而言,这种生活主题是很悲惨的...

Android 语言切换相关问题记录

前言Android切换语言是很常用的功能,项目中一般都会要做做英文和中文两种语言的切换,这里就简单的记录一下改变语言相关的问题。正文圆规正传监听语言切换的广播Intent.ACTION_LOCALE_CHANGED//如下是完整的定义public static final Strin...

bootprof文件分析系统开机时间

前言 开机时间较长,无法满足客户及内部测试指标要求。这里以bootprof文件为例,简单分析各个阶段耗时情况。正文这里以ATC平台为例。bootprof文件在root下,可以使用如下命令获取bootprofadb pull proc/bootprofbootprof内容简介---...

Android磁盘之U盘卸载

前言之前介绍了U盘的挂载,那当然不能少了U盘的卸载。这里大概的记录一下,方便自己查阅。Android P这里很多都是网上的,我就走走流程。正文VolumeManager.cpphandleBlockEvent()void VolumeManager::handleBlockEve...