【问题标题】:Can a Kotlin interface cache a value?Kotlin 接口可以缓存一个值吗?
【发布时间】:2017-04-18 15:50:43
【问题描述】:

我真的很喜欢在 Kotlin 中为接口使用默认实现,尤其是对于像 Observable 这样的常见模式。这是我的界面,

interface Observable<T>{

    // How do I cache this?
    val observers: MutableList<Observer<T>>
        get() = LinkedList<>()

    fun addObserver(o:Observer<T>){
        observers.add(o)
    }

    fun removeObserver(o:Observer<T>){
        observers.remove(o)
    }

    fun notifyObservers(u:T){
        for (o in observers){
            o.update(u)
        }
    }

}

接口引用observers的列表,但get()调用每次都返回一个新的LinkedList()。如何缓存 observers 的值,使其只创建一次?我试过使用kotlin-lazy,但要么语法不正确,要么不适合接口。我的 IDE 报错“接口中不允许使用委托属性。”

更新

根据 Yoav 的回答,我已将界面更改为

interface Observable<T>{
    val observers: MutableList<Observer<T>>
}

然后在实现类中,

class MyObservable : Observable<String>

   private val _observers = LinkedList<Observer<String>>()

   override val observers: MutableList<Observer<String>>
       get() = _observers

有什么技巧可以让这更简洁吗?

【问题讨论】:

    标签: java kotlin observable


    【解决方案1】:

    根据 Kotlin 文档:

    Kotlin 中的接口与 Java 8 非常相似。它们可以包含 抽象方法的声明,以及方法的实现。 它们与抽象类的不同之处在于接口 无法存储状态。

    接口不能保持任何状态,因为它们是完全抽象的。也许您应该使用abstract class 来缓存这些值?

    有关接口无状态原因的更多信息,请参阅this 问题。

    接口是一个契约,指定了它的实现者承诺什么 能够做到。它不需要指定状态,因为状态是 实施细节,仅用于限制实施者如何 本合同已履行。如果你想指定状态,你可以 想重新考虑您对接口的使用并查看抽象基础 代替类。

    【讨论】:

    • 谢谢。我已经更新了我的问题以反映我的具体实施。如果您有任何建议,请随时添加到此答案中。
    • @AdamHughes 为什么不只是override val observers: MutableList&lt;Observer&lt;String&gt;&gt; = LinkedList()?为什么需要显式支持字段?
    • 此外,您可以将帮助功能保留在界面中,甚至更好:将它们声明为扩展方法:)
    • Yoav,如果我设置 val observers = LinkedList(),这不会在每次调用 getter 时生成一个新的 LinkedList() 吗?我想重用这个列表。我会研究扩展方法,我不太熟悉你的提议(kotlin 新手)
    • 不,只有当你使用getter语法get() = LinkedList()
    【解决方案2】:

    虽然您不能直接在接口上创建有状态变量,但您可以通过定义包含缓存的伴随对象并在 this 上进行动态查找来获得相同的结果。使用您的示例:

    interface Observable<T>{
      // How do I cache this?
      val observers: MutableList<Observer<T>>
        get() = cache.computeIfAbsent(this) { mutableListOf<Observer<T>>() } as MutableList<Observer<T>>
    
      fun addObserver(o:Observer<T>){
        observers.add(o)
      }
    
      fun removeObserver(o:Observer<T>){
        observers.remove(o)
      }
    
      fun notifyObservers(u:T){
        for (o in observers){
          o.update(u)
        }
      }
    
      companion object {
        val cache = WeakHashMap<Observable<*>, MutableList<*>>()
      }
    }
    

    如果您想要更高效的缓存,可以使用Caffeine 之类的库。

    【讨论】:

      【解决方案3】:

      在考虑了更多之后,我发现了一个很好的模式。首先我尝试了直接的方法:

      interface A {
        val prop = compute() // Nope: Property initializers are not allowed in interfaces
        /*...*/
      }
      

      正如您所提到的,它可以使用自定义访问器来完成,但每次都必须重新计算:

      interface A {
        val prop get() = compute() // OK, but recomputed each time someProperty is read
        /*...*/
      }
      

      然后我想,也许我们可以使用委托属性来代替?但这也是不允许的:

      interface A {
        val prop by { compute() } // Not allowed: Delegated properties are not allowed in interfaces
        /*...*/
      }
      

      最后,我发现您可以通过使用委托创建属性扩展来实现相同的结果:

      interface A { /*...*/ }
      val A.prop by lazy { compute() }
      

      从使用的角度来看,您只需导入扩展程序,就可以像往常一样访问a.prop

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-12-01
        • 2011-01-10
        • 1970-01-01
        • 2018-12-14
        • 2013-11-02
        • 2023-02-02
        • 2019-08-31
        相关资源
        最近更新 更多