我有一套component interfaces套的

interface ITest<I> {
    operator fun invoke(p: I)
}

interface OTest<O> {
    operator fun invoke(): O
}

interface IOTest<I, O> {
    operator fun invoke(p: I): O
}

以及相应的functional interface

interface TestAdder<T> {
    fun add_f(p: T) {}  //default impl
}

继承以将上面的组件添加到相应的(functional) collection interfaces

interface ITestSet<I> : TestAdder<ITest<I>> {
    val i_v: I
    
    fun i_f1(p: I) {}  // default impl
    fun i_f2(p: I) {}  // default impl
    fun i_f3(p: I) {}  // default impl
}

interface OTestSet<O> : TestAdder<OTest<O>> {
    val o_v: O
    
    fun o_f1(p: O) {}  // default impl
    fun o_f2(p: O) {}  // default impl
    fun o_f3(p: O) {}  // default impl
}

interface IOTestSet<I, O> : TestAdder<IOTest<I, O>> {
    val i_v: I
    val o_v: O
    
    // same as ITestSet<I>
    fun i_f1(p: I) {}  // default impl
    fun i_f2(p: I) {}  // default impl
    fun i_f3(p: I) {}  // default impl 
    
    // same as OTestSet<O>
    fun o_f1(p: O) {}  // default impl
    fun o_f2(p: O) {}  // default impl
    fun o_f3(p: O) {}  // default impl

    fun io_f1(p: I): O
    ...
}

到目前为止,还没有必要:理想情况下,IOTestSet<I, O>应该继承ITestSet<I>OTestSet<O>中定义的功能:

interface IOTestSet<I, O> : ITestSet<I>, OTestSet<O>, TestAdder<IOTest<I, O>> {
    fun io_f1(p: I): O
    ...
}

但显然,TestAdder<T>interface在继承链中引入了不一致.


This smells like an age-old, archetypal paradigm (and probably even an XY) problem, still it seems I have to ask:

Q: How to compose this inheritance chain?

Or what is the established/much better/less and not unnecessarily convoluted/more elegant design pattern?

推荐答案

至于这不起作用的原因,那是因为您继承自相同的接口,只是类型参数不同.而你不能做that的原因解释了here.

Composition

当继承不起作用时,我们可以try 组合.(另见:"Prefer Composition Over Inheritance")

与其让ITestSetOTestSet继承TestAdder,这是"从相同接口继承,但类型参数不同"问题的根本原因,不如将类型TestAdder的仅获取属性添加到ITestSetOTestSetIOTestSet.

那么IOTestSet人将能够继承ITestSetOTestSet.完整代码如下:

// I have added variance modifiers where appropriate
interface ITest<in I> {
    operator fun invoke(p: I)
}

interface OTest<out O> {
    operator fun invoke(): O
}

interface IOTest<in I, out O> {
    operator fun invoke(p: I): O
}

interface TestAdder<in T> {
    fun add_f(p: T) {}  //default impl
}

interface ITestSet<I> {
    val i_v: I
    val inputAdder: TestAdder<ITest<I>>
    fun i_f1(p: I) {}  // default impl
    fun i_f2(p: I) {}  // default impl
    fun i_f3(p: I) {}  // default impl
}

interface OTestSet<O> : TestAdder<OTest<O>> {
    val o_v: O
    val outputAdder: TestAdder<OTest<O>>
    fun o_f1(p: O) {}  // default impl
    fun o_f2(p: O) {}  // default impl
    fun o_f3(p: O) {}  // default impl
}

interface IOTestSet<I, O> : ITestSet<I>, OTestSet<O> {
    val ioAdder: TestAdder<IOTest<I, O>>
    fun io_f1(p: I): O
}

Implementing the interfaces

在我看来,您仍然可以像以前一样实现这组新的接口--例如,要实现ITestSet<String>,您只需:

class Foo: ITestSet<String> {
    override val i_v: String = "Some String"
    override val inputAdder = object: TestAdder<ITest<String>> {
        // implementation goes here...
    }
}

请注意,可以为inputAdder提供默认实现:

interface ITestSet<I> {
    val i_v: I
    val inputAdder: TestAdder<ITest<I>> get() = object: TestAdder<ITest<I>> {
        // implement your thing here...
    }
    ...
}

但这会在您每次访问inputAdder时重新创建对象,这可能不是您想要的语义.

这给我带来了这个设计与您的原始设计的另一个不同之处:您允许实现以他们喜欢的任何方式实现inputAdderoutputAdderioAdder.这意味着它们的实现不一定是这样的,例如,每个ITestSet总是具有same inputAdder,就像在原始设计中一样,其中每个ITestSet的"inputAdder"始终是itself.当然,如果您完全控制代码,这不是问题.

Kotlin相关问答推荐

在Kotlin中,有没有一种函数方法将一个列表(N个元素)映射到一个相邻元素之和列表(N—1个元素)?

try 一次性插入多条记录时,JOOQ连接为空错误

如何在 Kotlin 中为类方法调用传递变量

Ktor 在 Heroku 上的 CORS 问题,但在本地没有

按钮无法在 Android Studio 上打开新活动

内容更改后的 var 重新计算

如何使用 Kotlin KClass 属性 simpleName 生成空值

具有多个不同类型来源的 LiveData

Koin Android:org.koin.error.NoBeanDefFoundException

Kotlin:泛型、反射以及类型 T 和 T:Any 之间的区别

使用最新的 com.jakewharton.rxbinding3:rxbinding:3.0.0-alpha2 库时未找到 RxTextView 和其他小部件

片段内的 Kotlin 按钮 onClickListener 事件

哪里可以找到aapt2日志(log)?

什么是开放式property?为什么我不能将其设置器设为private私有?

在Kotlin中传递并使用函数作为构造函数参数

Kotlin使用运行时断言进行空判断?

禁用 IntelliJ kotlin * 导入?

如何在Kotlin中获得KType?

Kotlin中OnclickListener方法之间的差异

如何在 Kotlin 中定义新的运算符?