繁体   English   中英

密封类泛型

[英]Sealed classes generics

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

这些事件建模如下。

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()
}

这是发出这些事件的类

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发出事件,同时让每个类都可以只发出自己的事件?

不确定您是否对为什么它不让您从基类发出项目感到困惑。 由于 E 可以是 BaseEvent 的任何子类型,如果您的类可以发出ConnectionStarted ,那么只要它被声明为BaseViewModel<AnythingBesidesConnectionStarted> ,就会违反其合同。

我能想到的唯一方法是拥有emit功能的私有和公共版本。 您可能需要更改班级中其他地方未显示的代码。 如果有某个函数返回E ,则必须对其进行更改,使其返回BaseEvent

abstract class BaseViewModel<E : BaseEvent> {

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

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

    fun emit(event: E){
        emitInternal(event)
    }
}

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

只需添加一个重载的私有版本,它接受BaseEvent参数(您需要一些@JvmName注释以使其可编译为 JVM 目标):

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)
    }
}

看起来您需要逆变,这可以使用in来实现。 假设你的基类,只有等方法, emit是使用类型E作为参数类型,而不是返回类型,则:

abstract class BaseViewModel<in E : BaseEvent> {

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM