Kotlin 泛型

本文介绍了Kotlin中的泛型,包括泛型的基本用法、类委托和委托属性、infix函数以及泛型的高级特性如泛型实化、协变和逆变。通过示例展示了如何使用Kotlin的泛型提升代码的扩展性和安全性。
摘要由CSDN通过智能技术生成

泛型和委托

泛型的基本用法

Java 早在 1.5 版本中就引入了泛型的机制,因此,Kotlin 自然也就支持了泛型功能。但是 Kotlin 中的泛型与 Java 中的泛型有同有异,这里先看与 Java 中相同的部分。

泛型,是指在一般的编程模式下,我们需要给任何一个变量指定一个具体的类型,而泛型允许我们在不指定具体类型的情况下进行编程,这样编写出来的代码将会拥有更好的扩展性。比如 List 就是使用泛型来实现的。

泛型主要有两种定义方式,一种是定义泛型类,另一种是定义泛型方法,使用的语法结构都是<T>,这个 T 只是一种约定俗成的泛型写法。

fun main(){
   
    // 泛型类的调用方式
    val myClass = MyClass<Int>()
    val result = myClass.method(123)
    println("$result")

    // 泛型方法的调用方式
    val myClass2 = MyClass2()
    // 类型推导机制,所以可省略泛型的指定。
//    val result = myClass.method<Int>(123)
    val result2 = myClass2.method(123)
    println("$result2")
}

/**
 * 定义泛型类
 */
class MyClass<T>{
   

    /**
     * 方法允许使用 T 类型的参数和返回值
     */
    fun method(param: T):T{
   
        return param
    }
}

/**
 * 定义泛型方法
 */
class MyClass2{
   

    /**
     * 泛型方法
     */
    fun <T> method(param: T):T{
   
        return param
    }

    /**
     * 指定上界的方式,对泛型的类型进行约束。
     * 这里表示,只能将方法的泛型指定为数字类型,比如 Int、Float、Double 等。
     *
     * 另外,默认情况下,所有泛型都是可以指定成可空类型的,这是因为在不手动指定上界时,泛型的上界默认是 Any?。
     * 如果想让泛型的类型不可为空,只需要将泛型的上界手动指定成 Any 就可以了。
     */
    fun <T:Number> method2(param: T):T{
   
        return param
    }
}
fun main(){
   
    contentResolver.query(uri,null,null,null,null)?.apply {
   
         while (moveToNext()){
   
             ... 
         }
         close()
    }

    contentResolver.query(uri,null,null,null,null)?.build {
   
        while (moveToNext()){
   
            ...
        }
        close()
    }
}

/**
 * 前面了解过的,此函数的作用与 apply 函数基本一样,区别在于此函数只能作用在 SB 上。
 */
fun StringBuilder.build(block:StringBuilder.() -> Unit):StringBuilder{
   
    block()
    return this
}

/**
 * 使用泛型,对上面函数进行扩展,让它实现和 apply 函数完全一样的功能。
 */
fun <T> T.build(block: T.() -> Unit):T {
   
    block()
    return this
}

类委托和委托属性

委托是一种设计模式,它的基本理念是:操作对象自己不会去处理某段逻辑,而是会把工作委托给另外一个辅助对象去处理。Java 对于委托并没有语言层级的实现,而像 C# 语言就对委托进行了原生的支持。Kotlin 中也是支持委托功能的,并且将之分为两种:类委托和委托属性。

类委托,它的核心思想在于将一个类的具体实现委托给另一个类去完成。Set 这种数据结构,它是一个接口,如果要实现它,需要使用它具体的实现类,比如 HashSet。借助委托模式,我们可以轻松实现一个自己的实现类:

/**
 * 构造函数中接收了一个 HashSet 参数,这就相当于一个辅助对象。
 * 然后在 Set 接口所有的方法实现中,都没有进行自己的实现,
 * 而是调动了辅助对象中相应的方法实现,这其实就是一种委托模式。
 *
 * 委托模式的意义在于:
 * 可以只是让大部分的方法实现调用辅助对象中的方法,少部分的方法实现由自己来重写,甚至加入一些自己独有的方法,
 * 这样,MySet 就会成为一个全新的数据结构类。
 *
 * 这种写法的弊端是,如果接口中待实现的方法太多,那么每个都要去这样调用辅助对象中的相应方法实现。
 * 这个问题,在 Java 中没有什么解决方案,但在 Kotlin 中可以通过类委托的功能来解决。
 */
//class MySet<T>(val helperSet: HashSet<T>):Set<T> {
   
//
//    override val size: Int
//        get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
//
//    override fun contains(element: T): Boolean {
   
//        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
//    }
//
//    override fun containsAll(elements: Collection<T>): Boolean {
   
//        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
//    }
//
//    override fun isEmpty(): Boolean {
   
//        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
//    }
//
//    override fun iterator(): Iterator<T> {
   
//        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
//    }
//}

/**
 * Kotlin 中委托使用的关键字是 by,只需要在接口声明的后面使用 by 关键字,
 * 再接上受委托的辅助对象,就可以免去之前所写的一大堆模板式的代码了。
 *
 * 这两段代码实现的效果是一致的。
 */
//class MySet<T>(val helperSet: HashSet<T>):Set<T> by helperSet{
   
//
//}

/**
 * 如果要对某个方法进行重新实现,只需单独重写。
 * 其它 Set 接口中的功能,则和 HashSet 保持一致。
 */
class MySet<T>(val helperSet: HashSet<T>):Set<T> by helperSet{
   

    /**
     * 新增方法
     */
    fun helloWorld() = println("Hello World")

    /**
     * 重写方法
     */
    override fun isEmpty() = false
}

委托属性的核心思想是将一个属性(字段)的具体实现委托给另一个类去实现。

class MyClass {
   

    /**
     * by 关键字连接了左边的 p 属性和右边的 Delegate() 实例。
     * 这种写法就代表,将 p 属性的具体实现委托给了 Delegate 类去完成。
     * 当调用 p 属性时,会自动调用 Delegate 类的 getValue(),
     * 当给 p 属性赋值时,会自动调用 Delegate 类的 setValue()。
     * 
     * 假设这里使用 val 关键字,那么只需要实现 getValue() 即可,
     * 因为 p 属性是无法在初始化后被重新赋值的。
     */
    var p by Delegate()
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值