• 重学Kotlin(五) 泛型 类型别名 委托


    泛型

    定义泛型

    class Box<E, T, W>(e: E, t: T, w: W) {
        var ee = e
        var tt = t
        var ww = w
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用泛型:

    //指定类型进行调用
    val box: Box<Int, Double, String> = Box(1, 1.0, "张飞")
    
    • 1
    • 2

    out和in

    • 在java中
    class Ani {
    
        }
    
        class Animal extends Ani {
    
        }
    
        class Fly extends Animal {
    
        }
    
        //  包括T的父类和它本身。
        //  包括T的子类和它本身。
        public void test1() {
            List<Fly> list = new ArrayList<>();
            List<Ani> list2 = new ArrayList<>();
            List<Animal> aniList = new ArrayList<>();
            List<Object> objectList = new ArrayList<>();
    
            doExtends(list);
            doSuper(list2);
    
            doExtends(aniList);
            doSuper(aniList);
    
            doSuper(objectList);
        }
    
        public void doExtends(List<? extends Animal> strings) {
    
    
        }
    
        public void doSuper(List<? super Animal> strings) {
    
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • Kotlin中:

      kotlin 中的 “out T” 等同于 Java 的 “?extends T”
      kotlin 中的 “in T” 等同于 Java 的 “?super T”

        open class Ani
    
        open class Animal : Ani()
    
        internal class Fly : Animal()
    
        //  包括T的父类和它本身。
        //  包括T的子类和它本身。
        //kotlin 中的 “out T” 等同于 Java 的 “?extends T”
        //kotlin 中的 “in T” 等同于 Java 的 “?super T”
        fun test1() {
            val list: MutableList<Fly> = ArrayList()
            val list2: MutableList<Ani> = ArrayList()
            val aniList: MutableList<Animal> = ArrayList()
            val objectList: MutableList<Any> = ArrayList()
    
    
            doExtends(list)
            doSuper(list2)
    
            doExtends(aniList)
            doSuper(aniList)
    
            doSuper(objectList)
        }
    
        fun doExtends(strings: MutableList<out Animal>) {}
    
        fun doSuper(strings: MutableList<in Animal>) {}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    泛型函数

    不仅类可以有类型参数。函数也可以有。类型参数要放在函数名称之前:

    fun <T> singletonList(item: T): List<T> {
        // ……
    }
    
    fun <T> T.basicToString(): String {  // 扩展函数
        // ……
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    要调用泛型函数,在调用处函数名之后指定类型参数即可:

    val l = singletonList<Int>(1)
    或者
    val l = singletonList(1)
    
    • 1
    • 2
    • 3

    泛型函数的上界

    //冒号之后指定的类型是上界:只有 Comparable 的子类型可以替代 T
    fun <T : Comparable<T>> sort(list: List<T>) {  …… }
    
    • 1
    • 2

    类型擦除

    Kotlin 为泛型声明用法执行的类型安全检测仅在编译期进行。 运行时泛型类型的实例不保留关于其类型实参的任何信息。 其类型信息称为被擦除。例如,Foo 与 Foo 的实例都会被擦除为 Foo<*>。

    类型别名

    类型别名为现有类型提供替代名称。

    缩减集合类型

    typealias NodeSet = Set<Network.Node>
    typealias FileTable<K> = MutableMap<K, MutableList<File>>
    
    • 1
    • 2

    函数类型提供另外的别名:

    typealias MyHandler = (Int, String, Any) -> Unit
    
    typealias Predicate<T> = (T) -> Boolean
    
    • 1
    • 2
    • 3

    为内部类和嵌套类创建新名称:

    class A {
        inner class Inner
    }
    class B {
        inner class Inner
    }
    
    typealias AInner = A.Inner
    typealias BInner = B.Inner
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    类型别名不会引入新类型。 它们等效于相应的底层类型

    typealias Predicate<T> = (T) -> Boolean
    
    fun foo(p: Predicate<Int>) = p(42)
    
    fun main() {
        val f: (Int) -> Boolean = { it > 0 }
        println(foo(f)) // 输出 "true"
    
        val p: Predicate<Int> = { it > 0 }
        println(listOf(1, -2).filter(p)) // 输出 "[1]"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    委托

    类委托

    委托模式已经证明是实现继承的一个很好的替代方式
    Kotlin将委托功能分为了两种:类委托和委托属性。
    类委托的核心思想在于将一个类的具体实现委托给另一个类去完成

    比如Set是一个接口,如果要使用它的话,需要使用它具体的实现类,比如HashSet。而借助于委托模式,可以轻松实现一个自己的实现类

    class MySet<T>(val helperSet: HashSet<T>) : Set<T> {
      
      override val size: Int
      		get() = helperSet.size
      
      override fun contains(element: T) = helperSet.contains(element)
      
      override fun containsAll(elements: Collection<T>) = helperSet.containsAll(elements)
      
      override fun isEmpty() = helperSet.isEmpty()
      
      override fun iterator() = helperSet.iterator()
      
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    可以看到,MySet的构造函数中接收了一个HashSet参数,这就相当于一个辅助对象。然后在Set接口所有的方法实现中,都没有进行自己的实现,而是调用了辅助对象中相应的方法实现,这其实就是一种委托模式

    但是这种写法也有一定的弊端,接口中的方法多了就会写一大堆。那么这个问题 有没有什么解决方案呢?在Java中确实没有,但是在Kotlin中可以通过类委托的功能来解决。

    Kotlin中委托使用的关键字是by,只需要在接口声明的后面使用by关键字,再接上受委托的辅助对象,就可以免去之前所写的一大堆模板式的代码了, 如下所示:

    class MySet<T>(val helperSet: HashSet<T>) : Set<T> by heplerSet {
      
    }
    
    • 1
    • 2
    • 3

    如果需要重写方法:

    class MySet<T>(val helperSet: HashSet<T>) : Set<T> by helperSet {
      
        fun helloWorld() = println("Hello World")
      	//重写isEmpty方法
        override fun isEmpty() = false
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    委托属性

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

    class MyClass {
      var p by Delegate()
    }
    
    • 1
    • 2
    • 3

    这里使用by关键字连接了左边的p属性和右边的Delegate实例,这种写法就代表着将p属性的具体实现委托给了Delegate类去完成。当调用p属性的时候会自动调用Delegate类的getValue()方法,当给p属性赋值的时候会自动调用Delegate类的setValue()方法。

    因此,还得对Delegate类进行具体的实现才行,代码如下所示:

    class Delegate {
      var propValue: Any? = null
      
      operator fun getValue(myClass: MyClass, prop: KProperty<*>): Any? {
        return propValue
      }
      
      operator fun setValue(myClass: MyClass, prop: KProperty<*>, value: Any?) {
        propValue = value
      }   
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这是一种标准的代码实现模板,在Delegate类中我们必须实现getValue()和setValue()这 两个方法,并且都要使用operator关键字进行声明。

    getValue()方法要接收两个参数:第一个参数用于声明该Delegate类的委托功能可以在什么类中使用,这里写成MyClass表示仅可在MyClass类中使用;第二个参数KProperty<>是 Kotlin中的一个属性操作类,可用于获取各种属性相关的值,在当前场景下用不着,但是必须在方法参数上进行声明。另外,<>这种泛型的写法表示你不知道或者不关心泛型的具体类型,只是为了通过语法编译而已,有点类似于Java中的写法。至于返回值可以声明成任何类型,根据具体的实现逻辑去写就行了,上述代码只是一种示例写法。

    setValue()方法也是相似的,只不过它要接收3个参数。前两个参数和getValue()方法是相 同的,最后一个参数表示具体要赋值给委托属性的值,这个参数的类型必须和getValue()方法返回值的类型保持一致。

    整个委托属性的工作流程就是这样实现的,现在当我们给MyClass的p属性赋值时,就会调用Delegate类的setValue()方法,当获取MyClass中p属性的值时,就会调用Delegate类的getValue()方法。

    不过,其实还存在一种情况可以不用在Delegate类中实现setValue()方法,那就是MyClass中的p属性是使用val关键字声明的。这一点也很好理解,如果p属性是使用val关键字声明的,那么就意味着p属性是无法在初始化之后被重新赋值的,因此也就没有必要实现 setValue()方法,只需要实现getValue()方法就可以了。

    kotlin中的by lazy

    by lazy代码块中,这样代码块中的代码在一开始的时候就不会执行,只有当laziness变量首次被调用的时候,代码块中的代码才会执行。

    val p by lazy { ... }
    
    • 1

    by lazy并不是连在一起的关键字,只有by才是Kotlin中的关键字,lazy在这里只是一个高阶函数而已。 在lazy函数中会创建并返回一个Delegate对象,当我们调用p属性的时候,其实调用的是Delegate对象的getValue()方法,然后getValue()方法中又会调用lazy函数传入的Lambda表达式,这样表达式中的代码就可以得到执行了,并且调用p属性后得到的值就是Lambda表达式中最后一行代码的返回值。

    这样看来,Kotlin的懒加载技术也并没有那么神秘,掌握了它的实现原理之后,我们也可以实现 一个自己的lazy函数。新建一个Later.kt文件,并编写如下代码:

    class Later<T>(val block: () -> T) { }
    
    • 1

    首先定义了一个Later类,并将它指定成泛型类。Later的构造函数中接收一个函数类型参数,这个函数类型参数不接收任何参数,并且返回值类型就是Later类指定的泛型。

    接着在Later类中实现getValue()方法,代码如下所示:

    class Later<T>(val block: () -> T) {
        var value: Any? = null
        operator fun getValue(any: Any?, prop: KProperty<*>): T {
            if (value == null) {
                value = block()
            }
            return value as T
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这里将getValue()方法的第一个参数指定成了Any?类型,表示希望Later的委托功能在所有类中都可以使用。然后使用了一个value变量对值进行缓存,如果value为空就调用构造函数中传入的函数类型参数去获取值,否则就直接返回。由于懒加载技术是不会对属性进行赋值的,因此这里就不用实现setValue()方法了。

    代码写到这里,委托属性的功能就已经完成了。虽然我们可以立刻使用它,不过为了让它的用法更加类似于lazy函数,最好再定义一个顶层函数。这个函数直接写在Later.kt文件中就可以了,但是要定义在Later类的外面,因为只有不定义在任何类当中的函数才是顶层函数。代码 如下所示:

    fun <T> later(block: () -> T) = Later(block)
    
    • 1

    我们将这个顶层函数也定义成了泛型函数,并且它也接收一个函数类型参数。这个顶层函数的 作用很简单:创建Later类的实例,并将接收的函数类型参数传给Later类的构造函数。

    现在,我们自己编写的later懒加载函数就已经完成了,你可以直接使用它来替代之前的lazy函数,如下所示:

    val uriMatcher by later {
        val matcher = UriMatcher(UriMatcher.NO_MATCH)
        matcher.addURI(authority, "book", bookDir)
        matcher.addURI(authority, "book/#", bookItem)
        matcher.addURI(authority, "category", categoryDir)
        matcher.addURI(authority, "category/#", categoryItem)
        matcher
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    通过委托可以代替多继承实现需求:

    interface CanFly {
        fun fly()
    }
    
    interface CanEat {
        fun eat()
    }
    
    open class Flyer : CanFly {
        override fun fly() {
            println("I can fly")
        }
    }
    
    open class Animal : CanEat {
        override fun eat() {
            println("I can eat")
        }
    }
    
    class Bird(flyer: Flyer, animal: Animal) : CanFly by flyer, CanEat by animal { }
    
    fun main() {
        val flyer = Flyer()
        val animal = Animal()
        val b = Bird(flyer, animal)
        b.fly()
        b.eat()
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    假设我们需要继承的类是A,委托对象是B、C、我们在具体调用的时候并不是像组合一样A.B.method,而是可以直接调用A.method,这更能表达A拥有该method的能力, 更加直观,虽然背后也是通过委托对象来执行具体的方法逻辑的;

    委托给另一个属性

    从 Kotlin 1.4 开始,一个属性可以把它的 getter 与 setter 委托给另一个属性,为将一个属性委托给另一个属性,应在委托名称中使用恰当的 :: 限定符,例如,this::delegate 或 MyClass::delegate。

    var topLevelInt: Int = 0
    
    class ClassWithDelegate(val anotherClassInt: Int)
    class MyClass(var memberInt: Int, val anotherClassInstance: ClassWithDelegate) {
        var delegatedToMember: Int by this::memberInt
        var delegatedToTopLevel: Int by ::topLevelInt
        
        val delegatedToAnotherClass: Int by anotherClassInstance::anotherClassInt
    }
    var MyClass.extDelegated: Int by ::topLevelInt
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    当想要以一种向后兼容的方式重命名一个属性的时候:引入一个新的属性、 使用 @Deprecated 注解来注解旧的属性、并委托其实现。

    class MyClass {
       var newName: Int = 0
       @Deprecated("Use 'newName' instead", ReplaceWith("newName"))
       var oldName: Int by this::newName
    }
    
    fun main() {
       val myClass = MyClass()
       // 通知:'oldName: Int' is deprecated.
       // Use 'newName' instead
       myClass.oldName = 42
       println(myClass.newName) // 42
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    将属性储存在映射中

    一个常见的用例是在一个映射(map)里存储属性的值。比如解析 JSON

    class User(val map: Map<String, Any?>) {
        val name: String by map
        val age: Int     by map
    }
    
    • 1
    • 2
    • 3
    • 4
    val user = User(mapOf(
        "name" to "John Doe",
        "age"  to 25
    ))
    
    • 1
    • 2
    • 3
    • 4
    println(user.name) // Prints "John Doe"
    println(user.age)  // Prints 25
    
    • 1
    • 2

    局部变量声明为委托属性

    fun example(computeFoo: () -> Foo) {
        val memoizedFoo by lazy(computeFoo)
    
        if (someCondition && memoizedFoo.isValid()) {
            memoizedFoo.doSomething()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    属性委托要求

    对于一个只读属性(即 val 声明的),委托必须提供一个操作符函数 getValue(),该函数具有以下参数:

    thisRef —— 必须与 属性所有者 类型(对于扩展属性——指被扩展的类型)相同或者是其超类型。
    property —— 必须是类型 KProperty<*> 或其超类型。
    getValue() 必须返回与属性相同的类型(或其子类型)。

    class Resource
    
    class Owner {
        val valResource: Resource by ResourceDelegate()
    }
    
    class ResourceDelegate {
        operator fun getValue(thisRef: Owner, property: KProperty<*>): Resource {
            return Resource()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    对于一个可变属性(即 var 声明的),委托必须额外提供一个操作符函数 setValue(), 该函数具有以下参数:

    thisRef —— 必须与 属性所有者 类型(对于扩展属性——指被扩展的类型)相同或者是其超类型。
    property —— 必须是类型 KProperty<*> 或其超类型。
    value — 必须与属性类型相同(或者是其超类型)。

    class Resource
    
    class Owner {
        var varResource: Resource by ResourceDelegate()
    }
    
    class ResourceDelegate(private var resource: Resource = Resource()) {
        operator fun getValue(thisRef: Owner, property: KProperty<*>): Resource {
            return resource
        }
        operator fun setValue(thisRef: Owner, property: KProperty<*>, value: Any?) {
            if (value is Resource) {
                resource = value
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    更多:
    https://blog.csdn.net/xingyu19911016/article/details/126414296
    https://www.kotlincn.net/docs/reference/delegated-properties.html

  • 相关阅读:
    【TS】笔记-TypeScript环境搭建
    json解析服务器List数据问题
    SSM学习笔记
    【C语言】文件操作详解
    在旋转过的有序数组中寻找目标值
    Flink中序列化RoaringBitmap不同方式的对比
    【Java 面试题】经典 Java 面试题 200 问(下)
    Pytest的断言与条件判断的区别
    unity代码混淆及帧同步服务器、常用软件记录
    app 更新 对aso是否有影响
  • 原文地址:https://blog.csdn.net/zx_android/article/details/127286203