浅析 Kotlin 的委托属性

版权声明:该博客为博主原创,转载请标明出处! https://blog.csdn.net/DJH2717/article/details/83306438

Kotlin 的委托属性其实从原理来讲, 很简单, 其就是将一个属性的委托给一个委托对象的约定的方法.

定义委托属性:  by 关键字

var delegateProperty: Int by MyDelegate()

在 by 关键字的右边表达式,  不一定要是对应的委托对象的构造方法, 其可以是任何值, 只需要这个值能够让 kotlin 编译器用正确的参数类型调用到对应的 getVlaue 和 setValue 方法即可.

MyDelegate 的实现:

class MyDelegate {
        operator fun getValue(delegateTest: DelegateTest, delegate: KProperty<*>): Int {
            println("----------")
            return 55
        }
        
        operator fun setValue(delegateTest: DelegateTest, delegate: KProperty<*>, newValue: Int) {
            println("The property is change, oldValue is ${delegateTest}, new value is $newValue")
        }
    }

其对应的 getValue 和 setValue 方法只需要满足如下两条约定即可:

  1. 用 operator 修饰.
  2. 方法的形参列表必须要符合约定, 如 demo 中所示,  如果少了 Kproperty, 无法通过编译.

接下来我们来看看完整 demo 的 java 字节码, 看看委托属性的来龙去脉:

class DelegateTest {
    
    
    var delegateProperty: Int by MyDelegate()
    
    class MyDelegate {
        operator fun getValue(delegateTest: DelegateTest, delegate: KProperty<*>): Int {
            println("----------")
            return 55
        }
        
        operator fun setValue(delegateTest: DelegateTest, delegate: KProperty<*>, newValue: Int) {
            println("The property is change, oldValue is ${delegateTest}, new value is $newValue")
        }
    }
}

对应的 java 代码:

public final class DelegateTest {
   
   static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(DelegateTest.class), "delegateProperty", "getDelegateProperty()I"))};
   @NotNull
   private final DelegateTest.MyDelegate delegateProperty$delegate = new DelegateTest.MyDelegate();

   public final int getDelegateProperty() {
      return this.delegateProperty$delegate.getValue(this, $$delegatedProperties[0]);
   }

   public final void setDelegateProperty(int var1) {
      this.delegateProperty$delegate.setValue(this, $$delegatedProperties[0], var1);
   }

   
   public static final class MyDelegate {
      public final int getValue(@NotNull DelegateTest delegateTest, @NotNull KProperty delegate) {
         Intrinsics.checkParameterIsNotNull(delegateTest, "delegateTest");
         Intrinsics.checkParameterIsNotNull(delegate, "delegate");
         String var3 = "----------";
         System.out.println(var3);
         return 55;
      }

      public final void setValue(@NotNull DelegateTest delegateTest, @NotNull KProperty delegate, int newValue) {
         Intrinsics.checkParameterIsNotNull(delegateTest, "delegateTest");
         Intrinsics.checkParameterIsNotNull(delegate, "delegate");
         String var4 = "The property is change, oldValue is " + delegateTest + ", new value is " + newValue;
         System.out.println(var4);
      }
   }
}

我们看到,  在一个类中定义一个委托属性, 这个字段并不会成为这个类的成员变量, 而是持有了对应的委托对象的引用,  同时会生成对应的get  和 set 方法, 然后通过调用委托对象对应的方法来获取值.

那么按理说我们每次访问委托属性时, 这个值都会从 get 方法中获取,  验证 demo 如下:

object Demo {
    @JvmStatic
    fun main(args: Array<String>) {
        val delegateTest = DelegateTest()
        println(delegateTest.delegateProperty)
        println(delegateTest.delegateProperty)
        println(delegateTest.delegateProperty)
    }
}

输出:

----------
55
----------
55
----------
55

我们看到, 的确如此, 在每次访问时, 都是重新调用了 get  方法来获取其值.


除了我们自定义一个委托类来实现委托属性外,  kotlin 还提供了几个标准的委托 api:

class DelegateDemo {
    
    val myLazy by lazy { 1 }  // 只能用在 val 变量
    
    var observable by Delegates.observable(66) { _, old, new ->
        println("Observable is change $old -> $new")
    }
    
    var vetoAble by Delegates.vetoable(88) { _, old, new ->
        println("Veto  is change $old  ->  $new")
        return@vetoable new > 88
    }
    
    var notNull by Delegates.notNull<Int>()  // 和 lateinit var 很类似, 但是其可以用在基本类型
    
}

主要说下,  Delegates.observable  和 vetoable 的区别:

  • 这两个标准的委托类, 都可以监听被委托的属性更改. 
  • observable 的回调是在 值 已经更改之后, 对值的更改没有干涉权利.
  • 而 vetoable 就如他的名字一样, "可否决"  的,  其回调在属性更改之前, 可以否定更改.

猜你喜欢

转载自blog.csdn.net/DJH2717/article/details/83306438