前言
简单记录一下泛型的使用,记录于此,方便自己查阅。
正文
什么是泛型
泛型即“参数化类型”,就是将具体的类型变成参数化类型,在声明一个泛型时,传递的是一个类型形参,在调用时传递的是一个类型实参。
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都是类型实参。
泛型分类
泛型类
使用泛型标记的类,被称为泛型类。泛型类的使用分为两种情况,一种是泛型类被用于实例化,另一种是泛型类被用于继承。
当泛型类用于实例化时,需要传递具体的类型实参。
泛型的符号一般使用大写字母,如<T>、<E>、<K>、<V>等,泛型符号可以是满足Kotlin命名规则的任意字符,甚至可以是某一个单词,如<TYPE>,一个类可以声明多个泛型,只要使用不同的泛型符号即可
-
泛型类被用于实例化
val list = ArrayList<String>() //String类型为泛型实参 val map = HashMap<String, Int>() //String、Int 为泛型实参 val set = HashSet<Long>() //Long 为泛型实参
-
泛型类被用于继承
当泛型类被用于继承时,需要为泛型形参提供一个具体类型或者另一个类型的形参。
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") } }
泛型接口
使用泛型标记的接口,被称为泛型接口。泛型接口的使用分为两种情况。
-
情况一
泛型接口被实现时已经确定泛型接口对应的实参,直接传递实参。
interface List<String> : Collection<String>{}
-
情况二
泛型接口被实现时不能够确定泛型接口对应的实参,则需要使用当前类或者接口的泛型形参
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可以称为绑定类型,绑定类型可以是类或者接口。
-
如果绑定类型是一个类,则类型参数T必须是Type的子类。
-
如果绑定类型是一个接口,则类型参数T必须是Type接口的实现类。
调用泛型上界类中的方法
上界是Number类型的。Double,Int等都是继承Number。如果传入其他类型,会提示不匹配。
-
一个约束条件
fun <T : Number> add(a: T, b: T): Double { return a.toDouble() + b.toDouble(); }
add(100, 100) add(10f, 100f) //类型不匹配[错误] add("a", "b")
-
多个约束条件
如果上界约束需要多个约束,则可以通过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>。
-
<T:Any?>表示类型实参是Any的子类,且类型实参可以为null。
-
<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() }
参考文章