首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >密封类仿制

密封类仿制
EN

Stack Overflow用户
提问于 2021-10-21 08:15:36
回答 3查看 390关注 0票数 0

在这个场景中,我有一个超级抽象类,它使用Kotlin sealed classes发出不同类型的事件。

这些事件的模型如下。

代码语言:javascript
复制
sealed class BaseEvent {
    object ConnectionStarted : BaseEvent()
    object ConnectionStopped : BaseEvent()
}

sealed class LegacyEvent : BaseEvent() {
    object TextChanged : LegacyEvent()
    object TextCleared : LegacyEvent()
}

sealed class AdvancedEvent : BaseEvent() {
    object ButtonClick : AdvancedEvent()
    object ButtonLongClick : AdvancedEvent()
}

下面是发出这些事件的类

代码语言:javascript
复制
abstract class BaseViewModel<E : BaseEvent> {

    private fun startConnection() {
        emit(BaseEvent.ConnectionStarted) // <-- Error
    }

    fun emit(event: E){
        //...
    }
}

class LegacyBaskan : BaseViewModel<LegacyEvent>() {
    fun textChanged() {
        emit(LegacyEvent.TextChanged) // <-- Works
    }
}

class AdvancedBaskan : BaseViewModel<AdvancedEvent>() {
    fun buttonClicked() {
        emit(AdvancedEvent.ButtonClick) // <-- Works
    }
}

在这里,它只对子类起作用,我可以在它们关联的类中发出LegacyEventAdvancedEvent中的任何事件。但是,对于BaseBaskan类,我不能从BaseEvent发出事件,尽管我声明泛型E必须扩展BaseEvent

我需要每个子类访问自己的事件和超类事件,而不是其他子类的事件。

如何仍然可以在基类中从BaseEvent 发出事件,同时赋予每个类仅发出自己事件的权限?

EN

回答 3

Stack Overflow用户

发布于 2021-10-21 12:49:02

不确定您是否对为什么不允许从基类发出项感到困惑。由于E可以是BaseEvent的任何子类型,如果您的类可以发出ConnectionStarted,那么它在声明为BaseViewModel<AnythingBesidesConnectionStarted>时就会违反它的约定。

我能想到的唯一办法就是拥有emit函数的私有和公共版本。您可能必须在类的其他地方更改尚未显示的代码。如果有返回E的函数,则必须对其进行更改,使其返回BaseEvent

代码语言:javascript
复制
abstract class BaseViewModel<E : BaseEvent> {

    private fun startConnection() {
        emitInternal(BaseEvent.ConnectionStarted)
    }

    private fun emitInternal(event: BaseEvent) {
      //...
    }

    fun emit(event: E){
        emitInternal(event)
    }
}
票数 0
EN

Stack Overflow用户

发布于 2021-10-21 12:51:48

您不能在BaseEvent.ConnectionStarted中发出BaseViewModel (以及其他事件),因为E还没有定义,所以类型系统不能确定您不会发出另一个破坏泛型类型不变性的子类型的事件。

只需添加一个重载的私有版本,该版本接受BaseEvent参数(您将需要一些@JvmName注释使其可用于JVM目标):

代码语言:javascript
复制
abstract class BaseViewModel<E : BaseEvent> {
    private fun startConnection() {
        emit(BaseEvent.ConnectionStarted)
    }

    @JvmName("emitBaseEvent")
    private fun emit(event: BaseEvent) {
        //...
    }

    fun emit(event: E) {
        emit(event as BaseEvent)
    }
}
票数 0
EN

Stack Overflow用户

发布于 2021-10-21 13:15:49

看起来您需要反方差,这可以使用in实现。假设基类只具有诸如emit之类的方法,这些方法使用E类型作为参数类型,而不是作为返回类型,那么:

代码语言:javascript
复制
abstract class BaseViewModel<in E : BaseEvent> {

https://kotlinlang.org/docs/generics.html#use-site-variance-type-projections

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69658281

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档