【问题标题】:Defining an interface as a property of an interface in kotlin and providing a concrete implemententation in interface implementation does not work在kotlin中将接口定义为接口的属性并在接口实现中提供具体的实现是行不通的
【发布时间】:2019-12-24 14:49:33
【问题描述】:

我有接口 A,其中接口 B 作为字段。当我执行 A 的实现时,假设具体 A,而不是提供接口 B,而是提供 B 的实现,编译器会抛出一个错误。我不明白为什么,因为在 Kotlin 中有继承,我认为这应该可行。顺便说一下,我用数据类试过这个。


interface B {
   ....
}

data class concreteB : B {
   .....
}

interface A {
   var someField: B
}

data class concreteA : A {
   //The error happens here, compiler says it's not type of overridden
   override var someField: concreteB
}

【问题讨论】:

  • 我在 Slack 上和 Kotlin 的人交谈过,解释是 LSP(Liskov 替换原则)参考:stackoverflow.com/questions/56860/…。似乎在 Kotlin 中不可能实现它,但我可能是错的。

标签: java inheritance kotlin interface


【解决方案1】:

A 的定义表明每个实现都必须有一个字段来存储实现B 的事物的实例。该字段为var,表示其他代码可以更改。

在您的 ConcreteA 中,您将覆盖它以限制该字段保存您的 ConcreteB

现在,调用 getter 的代码没问题,因为它总是会得到 B 类型的东西。

但是调用setter的代码是个问题。 如果他们尝试设置B 的一些其他实现怎么办?接口说他们可以,但你的具体实现不能。这就是编译器不允许您实现的原因。 (正如 Kotlin 人所说,这就是 Liskov 替换原则的实际应用。)

有几种方法可以编译:

  • 您可以在A 中使用val 而不是var,并且不允许其他任何人更改该值。 (简单,但可能不是您想要的。)

  • 您可以使用值的类型参数化A,将B 作为上限。如果他们愿意,这将使实现(和子接口)将类型限制为ConcreteB。 (这有点复杂,但非常灵活,可能最适合您的需求。)

  • 您可以将该字段的类型保留为B,并将其初始化为您的ConcreteB(根据Alexey 的回答)。 (但是,您的 ConcreteB 必须考虑到稍后设置其他 B 实现的可能性。)

【讨论】:

    【解决方案2】:

    您从父亲那里继承了某些东西并不意味着您就是您的父亲。

    这里数据类的使用有点混乱,因为你的代码不会用它们编译。

    这是您的代码工作的正确方式:

    interface B
    
    class ConcreteB : B
    
    interface A {
        var someField: B
    }
    
    class ConcreteA : A {
        override var someField: B = // That's the interface
            ConcreteB()             // That's the implementation
    }
    

    【讨论】:

    • 问题是这些数据类是弹簧实体,我想在其中引用其他实体,因为否则我会遇到保存这些实体的问题。你的例子是实现这个的唯一方法吗?例如,我想以通用方式在实体中声明字段 B。
    • 但这根本不是你在原始问题中要问的:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-22
    • 2016-02-02
    • 2020-05-23
    • 2011-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多