Kotlin之泛型

Android  Kotlin  2024年10月11日 am8:22发布2个月前更新 城堡大人
65 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、 本站名称: 笔友城堡
2、 本站网址: https://www.biumall.com/
3、 本站部分文章来源于网络,仅供学习与参考,如有侵权,请留言

暂无评论

暂无评论...

随机推荐

Binder个人简单总结

前言Binder对于Android开发,很熟悉,也很陌生。我也经常用,但对其原理不是很了解,因此参考其他大佬的,做一下笔记。简单记录一下Android中Binder的原理(其实主要摘抄),方便自己回顾。正文什么是BinderBinder是Android中的一种跨进程通信(IPC)的方式。...

[代码片段]SoundPool的使用

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

普希金:我曾经爱过你

我曾经爱过你:爱情,也许在我的心灵里还没有完全消亡,但愿它不会再打扰你,我也不想再使你难过悲伤。我曾经默默无语、毫无指望地爱过你,我既忍受着羞怯,又忍受着嫉妒的折磨,我曾经那样真诚、那样温柔地爱过你,但愿上帝保佑你找到的另一个人如我一样地爱你。...

Java的反射简介

前言本文主要参考其他作者的文章,然后自己整理一下,原文写得很仔细,但还得自己走一遍流程。感谢大佬分享。正文什么是Java的反射机制java允许开发者在程序运行过程中操作(访问和修改)类的各种属性以及方法。获取Class类对象java给我们提供了三种方式获取Class类对象。Sour...

JNI之函数介绍三之字符串操作

前言今天介绍一下JNI中字符串相关操作函数。正文NewStringjstring NewString(JNIEnv *env, const jchar *unicodeChars,jsize len);利用 Unicode 字符数组构造新的 java.lang.String 对象。un...

Android硬件加速

前言Android从3.0版本开始加入了硬件加速,这样就能够充分利用手机硬件GPU来提升界面的渲染效果。但是这种技术还有一些限制,比如有些开启了硬件加速的界面上无法使用Canvas的clipPath接口,一点调用就会抛出不支持的异常。现在来对硬件加速的小知识点做一些总结。硬件加速简单理解就是...