我非常喜欢为Kotlin中的接口使用默认实现,特别是对于可观察的常见模式。这是我的界面
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的值,使其只创建一次?我尝试过使用科特林-懒惰,但要么无法正确地使用语法,要么它不适用于接口。我的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有什么建议可以让这件事更简洁吗?
发布于 2017-04-18 16:18:10
根据Kotlin医生的说法:
Kotlin中的接口非常类似于Java 8,它们可以包含抽象方法的声明以及方法实现。与抽象类不同的是,接口不能存储状态。
接口不能保存任何状态,因为它们是完全抽象的。也许您应该使用abstract class来缓存这些值?
有关接口无状态的原因的更多信息,请参见这问题。
接口是一个契约,指定它的实现者承诺能够做什么。它不需要指定状态,因为state是一个实现细节,只用于约束实现者如何履行该契约。如果要指定状态,则可能需要重新考虑接口的使用,转而查看抽象基类。
发布于 2021-09-04 16:31:01
虽然不能在接口上直接创建有状态变量,但是可以通过定义包含缓存的配套对象并在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<*>>()
}
}如果您想要一个更有效的缓存,可以使用像咖啡因这样的库。
发布于 2022-03-26 23:04:09
再想一想,我发现了一个很好的模式。首先,我尝试了直接的方法:
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。
https://stackoverflow.com/questions/43476811
复制相似问题