kotlin 泛型学习笔记

泛型是什么:
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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值