泛型是什么:
1.泛型是函数定义的时候未指定具体类型,而在使用的时候可以传递多种具体类型。
2.泛型是java和kotlin中的概念,在其他类似编程语言中叫做参数多态(parametric polymorphism)或者模板。
注:本文翻译自英文书《Programing Kotlin》的第八章,并且进行了一些修改。
kotlin的泛型主要包含以下内容:
函数的泛型参数
fun random(one: Any, two:Any, three: Any): Any{//普通函数
return one;
}
fun <T> randomGeneric(one: T, two: T, three: T): T{//泛型参数函数
return one;
}
fun <K,V> put4Study(key: K, value: V):Unit{//多个泛型参数函数
Log.i(TAG,"key=$key,value=$value")
}
fun studyParameterisedFunction(){
Log.i(TAG,"---studyParameterisedFunction start---")
val r:Any = random("a","b","c");//缺点:使用变量r的时候需要自己判断r的具体类型进行转换,容易出错并且代码不简洁
Log.i(TAG,"r=$r")
// val r1:String = random("a","b","c");//无法编译通过
var r2:String = randomGeneric("a","b","c");//类型推断出是String类型
Log.i(TAG,"r2=$r2")
var r3:Int = randomGeneric(1,2,3);//类型推断出是Int类型
Log.i(TAG,"r3=$r3")
var r4:Any = randomGeneric("a",1,true);//向上类型推断出是Any类型(Any是所有类型的基类 )
Log.i(TAG,"r4=$r4")
put4Study(1,"a");//多个类型参数示例
Log.i(TAG,"---studyParameterisedFunction end---")
}
打印日志如下:
06-06 14:38:49.609 23448-23448/? I/TAG: ---studyParameterisedFunction start---
06-06 14:38:49.609 23448-23448/? I/TAG: r=a
06-06 14:38:49.609 23448-23448/? I/TAG: r2=a
06-06 14:38:49.609 23448-23448/? I/TAG: r3=1
06-06 14:38:49.609 23448-23448/? I/TAG: r4=a
06-06 14:38:49.609 23448-23448/? I/TAG: key=1,value=a
06-06 14:38:49.609 23448-23448/? I/TAG: ---studyParameterisedFunction end---
类的泛型参数
class Sequence<T>{//一个类型参数的类
}
class Dictionary<K,V>{//两个类型参数的类
}
fun studyParameterisedType() {
Log.i(TAG, "---studyParameterisedType start---")
val seqBoolean = Sequence<Boolean>()//实例化
val seqString = Sequence<String>()//实例化
val dict = Dictionary<Int,String>();//实例化
Log.i(TAG, "---studyParameterisedType end---")
}
有界性
泛型有一定局限性。例如一个函数是取出两个值中的最小值。但是两个泛型参数无法完成比较(因为可能是Any的任何类型)。解决方案就是限制泛型的类型,即泛型的有界性。
上边界
定义了两个函数:min是普通的泛型函数,minUpperBounds是存在上界的泛型函数。区别主要是
<T : Comparable<T>>
函数调用的实际参数必须实现接口Comparable,并且可以直接使用Comparable的方法compareTo,具体如下:
/**
* 未使用上界的泛型话
* 无法比较first和second的大小
*/
fun <T> min(first: T,second: T):T{
return first;//无法比较first和second的大小
}
/**
* 存在上界的泛型
* 可以接收所有Comparable的子类
*/
fun <T : Comparable<T>> minUpperBounds(first: T,second: T):T{
val result = first.compareTo(second);
if(result <=0){
return first;
}else{
return second;
}
}
fun studyUpperBounds() {
Log.i(TAG, "---studyUpperBounds start---")
val a:Int = minUpperBounds(3,6);//Int实现了Comparable接口
Log.i(TAG, "minUpperBounds(3,6)=$a")
val b:String = minUpperBounds("b","c");//String实现了Comparable接口
Log.i(TAG, "minUpperBounds(\"b\",\"c\")=$b")
val c: Any = min("c",1);//普通泛型函数
// val d: Any = minUpperBounds("d",1);//Any未实现Comparable接口,编译错误,参数必须是同类型的
Log.i(TAG, "---studyUpperBounds end---")
}
打印日志如下:
06-06 18:55:25.235 9406-9406/study.caiy.com.kotlinstudy I/TAG: ---studyUpperBounds start---
06-06 18:55:25.235 9406-9406/study.caiy.com.kotlinstudy I/TAG: minUpperBounds(3,6)=3
06-06 18:55:25.235 9406-9406/study.caiy.com.kotlinstudy I/TAG: minUpperBounds("b","c")=b
06-06 18:55:25.235 9406-9406/study.caiy.com.kotlinstudy I/TAG: ---studyUpperBounds end---
如上:minUpperBounds很好的实现了取两个数中最小值的功能
多个边界
1.函数的 多个上界的泛型参数
/**
* 函数 泛型参数 多个上界
* 关键字where
*/
fun<T> minSerializable(first: T, second: T) : T
where T : Comparable<T>, T : Serializable{
val result = first.compareTo(second);
if(result <=0){
return first;
}else{
return second;
}
}
/**
* 只实现了Comparable<Year>接口
*/
class Year(val value: Int) : Comparable<Year>{
override fun compareTo(other: Year): Int {
return this.value.compareTo(other.value)
}
}
/**
* 实现了Comparable<SerializableYear>和Serializable两个接口
*/
class SerializableYear(val value:Int): Comparable<SerializableYear>,Serializable{
override fun compareTo(other: SerializableYear): Int {
return this.value.compareTo(other.value)
}
}
调用端代码:
//函数的 有多个上界的泛型参数
// minSerializable(Year(2017),Year(2018))//编译错误,Year不是Serializable的子类
val minYear = minSerializable(SerializableYear(2017),SerializableYear(2018))
Log.i(TAG,"minSerializable(SerializableYear(2017),SerializableYear(2018))结果是${minYear.value}")
打印日志如下:
minSerializable(SerializableYear(2017),SerializableYear(2018))结果是2017
2.类的 多个上界的泛型参数
/**
* 类 泛型参数 多个上界
* 关键字 where T:Comparable<T>,T:Serializable
*/
class MultiBoundClass<T>(val value:T) where T:Comparable<T>,T:Serializable{
fun customCompareTo(other:MultiBoundClass<T>):Int{
return value.compareTo(other.value);
}
}
调用端代码:
// MultiBoundClass(Year(2017));//编译错误,Year不是Serializable的子类
val multiBound2017 = MultiBoundClass(SerializableYear(2017))
val multiBound2018 = MultiBoundClass(SerializableYear(2018))
val result = multiBound2017.customCompareTo(multiBound2018)
Log.i(TAG,"multiBound2017.customCompareTo(multiBound2018)结果是$result")
打印日志如下:
multiBound2017.customCompareTo(multiBound2018)结果是-1
型变
显而易见,Apple是Fruit的子类。但是Crate是否是Crate的子类,或者父类,还是两者没有任何关系。这就取决于泛型的类型-型变。
不变性
代码如下:
open class Fruit{
}
class Apple:Fruit(){
}
class Orange:Fruit(){
}
class Crate<T>(val elements:MutableList<T>){
fun add(t: T){
elements.add(t)
}
fun last(): T{
return elements.last()
}
}
fun foo(crate: Crate<Fruit>){
crate.add(Apple())
}
调用端代码如下
var oranges = Crate(mutableListOf(Orange(), Orange()))
// foo(oranges)//编译错误,Crate<Orange>不是Crate<Fruit>的子类型
var fruits = Crate(mutableListOf(Fruit(), Fruit()))
可以看到oranges无法作为函数foo的参数。小结如下:
默认情况下:Crate<Orange> 和 Crate<Fruit>是不同的类型,没有任何关系
协变
关键字out 生产者 只能作为函数的返回值,不能作为函数的入参
代码如下:
/**
* 协变
* 关键词out
*/
class CovariantCrate<out T>(val elements: List<T>){
//编译错误 T声明成了out,可以理解成生成者。对于我们只能作为返回值,不能作为入参
// fun add(t: T){
// elements.add(t)
// }
fun last(): T{
return elements.last()
}
}
fun fooCovariant(crate: CovariantCrate<Fruit>){
//do nothing
}
调用端代码如下:
val covariantOranges:CovariantCrate<Orange> = CovariantCrate(listOf(Orange(), Orange()))
fooCovariant(covariantOranges);//因为关键字out,CovariantCrate<Orange>被当做了CovariantCrate<Fruit>的子类
kotlin中很多不可变的集合类型都使用到了协变,例如Sets, Lists等
函数返回型变
自我感觉和泛型关系不大
代码如下:
open class Animal{
}
class Sheep : Animal(){
fun onlyInSheep(){
}
}
open class Farm {
open fun get(): Animal{
return Animal()
}
}
class SheepFarm() : Farm() {
override fun get(): Sheep{//函数返回值 型变 Sheep是Animal的子类
return Sheep()
}
}
调用端代码如下:
val farm: Farm = SheepFarm()
val animal1 = farm.get()
// animal1.onlyInSheep();//无法编译通过,因为farm.get()得到的是一个Animal类型,没有onlyInSheep方法
val sheepFarm = SheepFarm()
val animal2 = sheepFarm.get()
animal2.onlyInSheep();
逆变性
关键字in 消费者 只能用作函数的入参,不能作为函数的返回值
未使用逆变性,代码如下:
interface Listener<T> {
fun onNext(t: T): Unit
}
class EventStream<T>(val listener: Listener<T>) {
fun start(t: T): Unit{
listener.onNext(t);
}
fun stop(): Unit{
}
}
未使用逆变性,调用端代码如下:
//非型变示例
val stringListener = object : Listener<String> {
override fun onNext(t: String){
Log.i(TAG,t)
}
}
val stringStream = EventStream<String>(stringListener)//stringListener是Listener<String>类型,Listener<T>的子类一
stringStream.start("a")
val dateListener = object : Listener<Date> {
override fun onNext(t: Date){
Log.i(TAG,t.toString())
}
}
val dateStream = EventStream<Date>(dateListener)//dateListener是Listener<Date>类型,Listener<T>的子类二
dateStream.start(Date())
//可以看到这里定义了两个listener:stringListener、dateListener
未使用逆变性,打印日志如下:
06-07 21:15:32.805 10692-10692/study.caiy.com.kotlinstudy I/TAG: a
06-07 21:15:32.805 10692-10692/study.caiy.com.kotlinstudy I/TAG: Wed Jun 07 21:15:32 GMT+08:00 2017
使用逆变性,代码如下:
/**
* 逆型变 关键词 in 消费者 只能作为函数入参,不能作为返回值
*/
interface Listener4Contra<in T> {
fun onNext(t: T): Unit
}
class EventStream4Contra<in T>(val listener: Listener4Contra<T>) {
fun start(t: T): Unit{
listener.onNext(t);
}
fun stop(): Unit{
}
}
使用逆变性,调用端代码如下:
/逆型变(关键字in)可以只用一个listener实现功能
val loggingListener = object : Listener4Contra<Any> {
override fun onNext(t: Any){
Log.i(TAG,t.toString())
}
}
EventStream4Contra<String>(loggingListener).start("b")//String是Any的子类
EventStream4Contra<Date>(loggingListener).start(Date())//Date是Any的子类
使用逆变性,打印日志如下:
06-07 21:15:32.806 10692-10692/study.caiy.com.kotlinstudy I/TAG: b
06-07 21:15:32.806 10692-10692/study.caiy.com.kotlinstudy I/TAG: Wed Jun 07 21:15:32 GMT+08:00 2017
型变小结
如上图所示:
如果Orange是Fruit的子类,那么:
不变性(默认情况): Crate<Orange>
和Crate<Fruit>
没有任何关系
协变性(out): Crate<Orange>
是Crate<Fruit>
的子类型
逆变性(in):Crate<Fruit>
是Crate<Orange>
的子类型
Nothing类型
关键字Nothing的定义如下:Nothing是一个类,并且没有实例
/**
* Nothing has no instances. You can use Nothing to represent "a value that never exists": for example,
* if a function has the return type of Nothing, it means that it never returns (always throws an exception).
*/
public class Nothing private constructor()
代码如下:
class Box<out T>(){
}
interface Marshaller<out T> {
fun marshall(json: String): T?//函数返回参数类型为T?
}
object StringMarshaller : Marshaller<String>{
override fun marshall(json: String): String? {//函数返回参数类型为String?
return json;
}
}
object NoopMarshaller : Marshaller<Nothing> {
override fun marshall(json: String):Nothing? { //函数返回参数Nothing?
//Nothing不能实例化,只能返回null
return null
}
}
调用端代码如下
/**
* 关键字Nothing Nothing是所有类型的子类
*
*
*/
fun studyNothing(){
Log.i(TAG, "---studyNothing start---")
//Nothing泛型实例化示例
Box<String>()//正常实例化
Box<Nothing>()//Nothing实例化
//Nothing 函数返回示例
val a = StringMarshaller.marshall("a")
Log.i(TAG,"a=$a")
val b = NoopMarshaller.marshall("b")
Log.i(TAG,"b=$b")
//Nothing 空集合示例
var dateList:List<Date> = emptyList()
var stringList:List<String> = emptyList()
var dateSet:Set<Date> = emptySet()
var stringSet:Set<String> = emptySet()
Log.i(TAG, "---studyNothing end---")
}
打印日志如下:
06-08 10:29:38.110 15206-15206/study.caiy.com.kotlinstudy I/TAG: ---studyNothing start---
06-08 10:29:38.110 15206-15206/study.caiy.com.kotlinstudy I/TAG: a=a
06-08 10:29:38.110 15206-15206/study.caiy.com.kotlinstudy I/TAG: b=null
06-08 10:29:38.110 15206-15206/study.caiy.com.kotlinstudy I/TAG: ---studyNothing end---
类型投影
1.out 类型投影示例
代码如下:
//第三方提供
class Crate<T>(val elements:MutableList<T>){
fun add(t: T){
elements.add(t)
}
fun last(): T{
return elements.last()
}
}
fun getWithCrate(crate: Crate<Fruit>){
//do nothing
}
fun getWithCrateWhenProjection(crate: Crate<out Fruit>){
// crate.add(Fruit())//编译不通过,因为是out类型,add方法被禁止访问
Log.i(TAG,"crate.last()结果是:" + crate.last())
}
调用端代码如下:
val oranges = Crate(mutableListOf(Orange(),Orange()))
// getWithCrate(oranges)//编译错误,Crate<Orange>不能转换为Crate<Fruit>
//假定Crate是第三方提供的类,不可修改。
//方法中加入关键字out:Crate<Orange>类型投影成了Crate<Fruit>
getWithCrateWhenProjection(oranges)
日志打印如下:
06-08 13:48:54.850 5768-5768/study.caiy.com.kotlinstudy I/TAG: crate.last()结果是:study.caiy.com.kotlinstudy.Orange@69731f7
2.in 类型投影示例
代码如下:
//第三方提供
interface Listener<T> {
fun onNext(t: T): Unit
}
class EventStream<T>(val listener: Listener<T>) {
fun start(t: T): Unit{
listener.onNext(t);
}
fun stop(): Unit{
}
}
class EventStream4Projection<T>(val listener: Listener<in T>) {
fun start(t: T): Unit{
listener.onNext(t);
}
fun stop(): Unit{
}
}
调用端代码如下:
val loggingListener = object :Listener<Any> {
override fun onNext(t: Any){
Log.i(TAG,t.toString())
}
}
// EventStream<String>(loggingListener)//无法编译通过,需要Listener<String>,实际传递Listener<Any>
//假定Listener是第三方提供的接口,无法修改
//loggingListener一个listener适用于两个
EventStream4Projection<String>(loggingListener).start("b")//方法中加入关键字in : Listener<Any>类型投影成了Listener<String>
EventStream4Projection<Date>(loggingListener).start(Date())//方法中加入关键字in : Listener<Any>类型投影成了Listener<Date>
日志打印如下:
06-08 13:48:54.850 5768-5768/study.caiy.com.kotlinstudy I/TAG: b
06-08 13:48:54.850 5768-5768/study.caiy.com.kotlinstudy I/TAG: Thu Jun 08 13:48:54 GMT+08:00 2017
类型擦除
1.函数 类型擦除
fun printInts(list: Set<Int>): Unit {
for (int in list) Log.i(TAG,int.toString())
}
fun printStrings(list: Set<String>): Unit {
for (string in list) Log.i(TAG,string)
}
执行命令 javap -c GenericKt (在GenericKt.class文件所在路径下执行)
可以看到生成的字节码文件如下:
public static final void printInts(java.util.Set<java.lang.Integer>);//方法printInts
Code:
0: aload_0
1: ldc_w #404 // String list
4: invokestatic #15 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
7: aload_0
8: invokeinterface #410, 1 // InterfaceMethod java/util/Set.iterator:()Ljava/util/Iterator;
13: astore_2
14: aload_2
15: invokeinterface #416, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
20: ifeq 50
23: aload_2
24: invokeinterface #419, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
29: checkcast #117 // class java/lang/Number
32: invokevirtual #121 // Method java/lang/Number.intValue:()I
35: istore_1
36: invokestatic #29 // Method study/caiy/com/kotlinstudy/MainActivityKt.getTAG:()Ljava/lang/String;
39: iload_1
40: invokestatic #422 // Method java/lang/String.valueOf:(I)Ljava/lang/String;
43: invokestatic #55 // Method android/util/Log.i:(Ljava/lang/String;Ljava/lang/String;)I
46: pop
47: goto 14
50: return
public static final void printStrings(java.util.Set<java.lang.String>);//方法printStrings
Code:
0: aload_0
1: ldc_w #404 // String list
4: invokestatic #15 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
7: aload_0
8: invokeinterface #410, 1 // InterfaceMethod java/util/Set.iterator:()Ljava/util/Iterator;
13: astore_2
14: aload_2
15: invokeinterface #416, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
20: ifeq 44
23: aload_2
24: invokeinterface #419, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
29: checkcast #107 // class java/lang/String
32: astore_1
33: invokestatic #29 // Method study/caiy/com/kotlinstudy/MainActivityKt.getTAG:()Ljava/lang/String;
36: aload_1
37: invokestatic #55 // Method android/util/Log.i:(Ljava/lang/String;Ljava/lang/String;)I
40: pop
41: goto 14
44: return
可以看到关键代码执行了类型转换,如下:
29: checkcast #117 // class java/lang/Number
29: checkcast #107 // class java/lang/String
生成的字节码文件可以等价于下面的代码
fun printInts(list: Set<Any>): Unit {
for (obj in list) {
println(obj as Int)
}
}
fun printStrings(list: Set<Any>): Unit {
for (obj in list) {
println(obj as String)
}
}
可以理解成泛型Set《Int》擦除成了Set《Any》,泛型Set《String》擦除成了Set《Any》
2.函数声明 类型擦除
fun <T : Comparable<T>>max(list: List<T>): T {
var max = list.first()
for (t in list) {
if (t >max)
max = t
}
return max
}
可以看到生成的字节码文件如下:
public static final <T extends java.lang.Comparable<? super T>> T max(java.util.List<? extends T>);
Code:
0: aload_0
1: ldc_w #404 // String list
4: invokestatic #15 // Method kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull:(Ljava/lang/Object;Ljava/lang/String;)V
7: aload_0
8: invokestatic #431 // Method kotlin/collections/CollectionsKt.first:(Ljava/util/List;)Ljava/lang/Object;
11: checkcast #168 // class java/lang/Comparable
14: astore_1
15: aload_0
16: invokeinterface #434, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
21: astore_3
22: aload_3
23: invokeinterface #416, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
28: ifeq 57
31: aload_3
32: invokeinterface #419, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
37: checkcast #168 // class java/lang/Comparable
40: astore_2
41: aload_2
42: aload_1
43: invokeinterface #172, 2 // InterfaceMethod java/lang/Comparable.compareTo:(Ljava/lang/Object;)I
48: iconst_0
49: if_icmple 54
52: aload_2
53: astore_1
54: goto 22
57: aload_1
58: areturn
可以看到关键代码执行了类型转换,如下:
37: checkcast #168 // class java/lang/Comparable
可以理解成:类型擦除成了Comparable
防止类型擦除可以使用@JvmName注解,或者使用下面的类型具化
类型具化
关键字reified,需要和内联函数配合使用
代码如下:
inline fun <T>runtimeTypeNormal(): Unit {
// println("My type parameter is " + T::class.qualifiedName)//编译错误,无法调用::class
}
/**
*
* 运行时获取T的实际类型
* reified关键字,必须和inline配合使用
*/
inline fun <reified T>runtimeType4Refication(): Unit {
println("My type parameter is " + T::class.qualifiedName)
}
/**
* 运行时类型检查
*/
inline fun <reified T>List<Any>.collect(): List<T> {
return this.filter { it is T }.map { it as T }
}
/**
* 运行时类型检查 示例2
*/
inline fun <reified T>printT(any: Any): Unit {
if (any is T) {
Log.i(TAG,"I am a T: $any")
}else{
Log.i(TAG,"not match")
}
}
调用端代码如下:
/**
* 类型具化
*/
fun studyTypeReification(){
Log.i(TAG, "---studyTypeReification start---")
//运行时获取T的实际类型
runtimeType4Refication<String>();
runtimeType4Refication<Int>();
//运行时类型检查
val list = listOf("green", false, 100, "blue")
val strings = list.collect<String>()
Log.i(TAG,"strings=$strings")
//字节码示例
printT<String>("a");
printT<String>(1);
Log.i(TAG, "---studyTypeReification end---")
}
打印日志如下:
06-08 16:34:04.665 13999-13999/study.caiy.com.kotlinstudy I/TAG: ---studyTypeReification start---
06-08 16:34:04.665 13999-13999/study.caiy.com.kotlinstudy I/System.out: My type parameter is kotlin.String
06-08 16:34:04.665 13999-13999/study.caiy.com.kotlinstudy I/System.out: My type parameter is kotlin.Int
06-08 16:34:04.666 13999-13999/study.caiy.com.kotlinstudy I/TAG: strings=[green, blue]
06-08 16:34:04.666 13999-13999/study.caiy.com.kotlinstudy I/TAG: I am a T: a
06-08 16:34:04.666 13999-13999/study.caiy.com.kotlinstudy I/TAG: not match
06-08 16:34:04.666 13999-13999/study.caiy.com.kotlinstudy I/TAG: ---studyTypeReification end---
printT函数生成的字节码文件中关键部分如下:
4: instanceof #110 // class java/lang/String
可以看到使用关键字reified,类型在运行期保留了。
嵌套泛型边界
注:英文是Recursive type bounds,即翻译成递归泛型边界
关键代码部分为:<E : Account4<E>>
下面的代码示例主要以四种方式分别实现了一个功能:账号排序(其中账号有不同的类型,只有相同的类型直接可以排序)。可以看到嵌套泛型约束的方式(第四种方式)实现了效果,且代码比较优雅。
代码如下
interface Account {
val balance: BigDecimal
}
data class SavingsAccount(override val balance: BigDecimal,val interestRate: BigDecimal) : Account,Comparable<SavingsAccount> {
override fun compareTo(other: SavingsAccount): Int =
balance.compareTo(other.balance)
}
data class TradingAccount(override val balance: BigDecimal, val margin:Boolean) : Account, Comparable<TradingAccount> {
override fun compareTo(other: TradingAccount): Int =
balance.compareTo(other.balance)
}
interface Account2 : Comparable<Account2> {
val balance: BigDecimal
override fun compareTo(other: Account2): Int =
balance.compareTo(other.balance)
}
data class SavingsAccount2(override val balance: BigDecimal,val interestRate: BigDecimal) : Account2{
}
data class TradingAccount2(override val balance: BigDecimal, val margin:Boolean) : Account2{
}
interface Account3<E> : Comparable<E> {
val balance: BigDecimal
// override fun compareTo(other: E): Int = balance.compareTo(other.balance)//编译错误,编译期无法判断泛型参数other有balance属性
}
//data class SavingsAccount3(override val balance: BigDecimal, val interestRate: BigDecimal) : Account3<SavingsAccount3>
//data class TradingAccount3(override val balance: BigDecimal, val margin:Boolean) : Account3<TradingAccount3>
interface Account4<E : Account4<E>> : Comparable<E> {
val balance: BigDecimal
override fun compareTo(other: E): Int = balance.compareTo(other.balance)
}
data class SavingsAccount4(override val balance: BigDecimal, val interestRate: BigDecimal) : Account4<SavingsAccount4>
data class TradingAccount4(override val balance: BigDecimal, val margin:Boolean) : Account4<TradingAccount4>
调用端代码如下:
/**
* 嵌套泛型边界
* 举例 <E : Account4<E>>
*/
fun studyRecursiveTypeBounds(){
Log.i(TAG, "---studyRecursiveTypeBounds start---")
//方式一
//缺点:compareTo在每个子类中都实现了,代码重复
val savings1 = SavingsAccount(BigDecimal(105), BigDecimal(0.04))
val savings2 = SavingsAccount(BigDecimal(396), BigDecimal(0.05))
savings1.compareTo(savings2)
val trading1 = TradingAccount(BigDecimal(211), true)
val trading2 = TradingAccount(BigDecimal(853), false)
trading1.compareTo(trading2)
// savings1.compareTo(trading1)//编译错误
//方式二
//缺点:savings和trading是不同的子类型,(需求是不比较不同子类型的acount)
val savings = SavingsAccount2(BigDecimal(105), BigDecimal(0.04))
val trading = TradingAccount2(BigDecimal(210), true)
savings.compareTo(trading)
//方式三
//无法编译成功 Account3及其子类
//方式四
val saving4s1 = SavingsAccount4(BigDecimal(105), BigDecimal(0.04))
val saving4s2 = SavingsAccount4(BigDecimal(396), BigDecimal(0.05))
val result1 = saving4s1.compareTo(saving4s2)
Log.i(TAG,"result1=$result1")
val trading41 = TradingAccount4(BigDecimal(853), true)
val trading42 = TradingAccount4(BigDecimal(518), false)
val result2 = trading41.compareTo(trading42)
Log.i(TAG,"result2=$result2")
// saving4s1.compareTo(trading42)//编译错误 达到目标
//注意 BettingAccount4Best和BettingAccount类的区别
Log.i(TAG, "---studyRecursiveTypeBounds end---")
}
打印日志如下:
06-08 17:38:56.624 751-751/study.caiy.com.kotlinstudy I/TAG: ---studyRecursiveTypeBounds start---
06-08 17:38:56.625 751-751/study.caiy.com.kotlinstudy I/TAG: result1=-1
06-08 17:38:56.625 751-751/study.caiy.com.kotlinstudy I/TAG: result2=1
06-08 17:38:56.625 751-751/study.caiy.com.kotlinstudy I/TAG: ---studyRecursiveTypeBounds end---
注:存在一个kotlin和java都有的缺陷
/**
* 缺陷:BettingAccount和泛型参数SavingsAccount4可以不同
*/
abstract class BettingAccount : Account4<SavingsAccount4>
/**
* 最佳实践:BettingAccount4Best和泛型参数BettingAccount4Best相同
*/
abstract class BettingAccount4Best : Account4<BettingAccount4Best>
代数数据类型
代数数据类型在数据结构中比较常用,下面是一个自己实现的List,内部实现是链表结构
代码如下:
/**
* 内部是链表类型的List
*/
sealed class MyList<out T> {
fun isEmpty() = when (this) {
is Empty -> true
is Node -> false
}
fun size(): Int= when (this) {
is Empty -> 0
is Node -> 1 + this.next.size()
}
fun tail(): MyList<T> = when (this) {
is Node -> this.next
is Empty -> this
}
fun head(): T = when (this) {
is Node<T> ->this.value
is Empty -> throw RuntimeException("Empty list")
}
operator fun get(pos: Int): T {
require(pos>= 0, { "Position must be >=0" })
return when (this) {
is Node<T> -> if (pos == 0) head() else this.next.get(pos - 1)
is Empty -> throw IndexOutOfBoundsException()
}
}
//方式一:注解
fun append(t: @UnsafeVariance T): MyList<T> = when (this) {//这里的@UnsafeVariance注释阻止了编译期报错
is Node<T> -> Node(this.value, this.next.append(t))
is Empty -> Node(t, Empty)
}
companion object {
operator fun <T>invoke(vararg values: T): MyList<T> {
var temp: MyList<T> = Empty
for (value in values) {
temp = temp.append(value)
}
return temp
}
}
}
private class Node<out T>(val value: T, val next: MyList<T>) : MyList<T>()
private object Empty : MyList<Nothing>()
//方式二:扩展函数
fun <T>MyList<T>.appendOuter(t: T): MyList<T> = when (this) {
is Node<T> -> Node(this.value, this.next.append(t))
is Empty -> Node(t, Empty)
}
调用端代码如下:
/**
* 代数数据类型
*/
fun studyAlgebraicDataTypes(){
Log.i(TAG, "---studyAlgebraicDataTypes start---")
var myList = MyList.invoke("this","is");
myList = myList.append("my")
myList = myList.appendOuter("list")
Log.i(TAG, myList.size().toString())
Log.i(TAG, myList.head())
Log.i(TAG, myList[1])
Log.i(TAG, myList.get(2))
Log.i(TAG, "---studyAlgebraicDataTypes end---")
}
打印日志如下:
06-08 18:53:04.253 10974-10974/study.caiy.com.kotlinstudy I/TAG: ---studyAlgebraicDataTypes start---
06-08 18:53:04.255 10974-10974/study.caiy.com.kotlinstudy I/TAG: 4
06-08 18:53:04.255 10974-10974/study.caiy.com.kotlinstudy I/TAG: this
06-08 18:53:04.255 10974-10974/study.caiy.com.kotlinstudy I/TAG: is
06-08 18:53:04.255 10974-10974/study.caiy.com.kotlinstudy I/TAG: my
06-08 18:53:04.255 10974-10974/study.caiy.com.kotlinstudy I/TAG: ---studyAlgebraicDataTypes end---
总结
泛型提高了程序的健壮性、复用性。
其它
本文代码已上传到github: https://github.com/fightingBirdCaiy/KotlinStudy