【问题标题】:Overriding a method which has a parameter of the abstract base class覆盖具有抽象基类参数的方法
【发布时间】:2019-09-13 01:40:22
【问题描述】:

所以,问题是: 我有一个抽象基类 (Property),它有一个抽象成员方法 (same())。 same() 方法返回布尔值并应检查是否相等,但“相等”在子类中的解释可能不同,因此我不会覆盖 equals()compareTo()。因为它测试与它类型的另一个对象是否相等,所以签名看起来像这样:

public abstract boolean same(Property other);

现在,当我试图在课堂上覆盖它时,比如说“SubProperty”,我不能使用SubProperty 作为参数的类型。我知道一般原因以及添加类型边界的建议,但由于参数类型是它自己的类,因此事情变得越来越复杂。

有什么流畅的方法可以正确覆盖方法吗?

因为它在其他 StackOverflow 问题中被推荐,所以我尝试使用类型边界(在这种情况下是循环的)。所以,这个类应该是这样的:

public abstract class Property<T extends Property<T>>

same() 方法:

public abstract boolean same(T other);

对于子类,这意味着:

public class SubProperty extends Property<SubProperty>

还有same()方法:

public boolean same(SubProperty other);

如果我按照建议使用类型边界,这将是结果。它实际上可以构建,但我什至不知道它是否能正常工作。无论如何,它看起来很糟糕且无法维护(尤其是因为我在 Property 中使用了另外两个泛型类型参数)。

我希望有其他方法可以做到这一点。如果重要的话,我使用的是 java 1.8 (openjdk 1.8.0.212-b04)

编辑 1:

因为人们问:same() 不像 equals()compareTo()。是的,它在某种意义上是关于平等的。但实际上我认为这样描述更容易:

same() 是一种检查this 中的something 和同一类的其他一些对象并根据方法中发生的情况返回truefalse 的方法。

编辑 2:

正如@davidxxx 解释的那样,使代码变得“更流畅”似乎是不可能的。我可能会留下基类参数并检查我在方法中得到的对象。毕竟看起来还不错。

【问题讨论】:

  • 我真的很困惑为什么你不能为此覆盖equals
  • "但是“平等”在子类中的解释可能不同,因此我没有覆盖equals()compareTo()"我并没有真正理解你的意思。你能用代码证明你的观点吗?

标签: java oop generics


【解决方案1】:

不幸的是,您没有其他方法,因为在 Java 中,返回类型是协变的,但参数类型不是因为它与 liskov principle 相反。

Liskov 的行为子类型概念定义了 对象的可替代性; 也就是说,如果 S 是 T 的子类型,那么 程序中 T 类型的对象可以被 S 类型的对象替换 不改变该程序的任何理想属性(例如 正确性)。

下面是一个简单的例子,说明了为什么这条规则很重要。

假设子类:

public class FooProperty extends Property{
    public boolean same(FooProperty other){...}
}

假设你以这种方式实例化它:

Property property = new FooProperty();

你可以这样做:

property.same(new FooProperty());

但你也可以这样做:

property.same(new Property());

但是对于期望 FooPropertyFooProperty.same() 覆盖的预期参数而言,这并不一致。
这违反了 liskov 原则,因为我应该能够用它的任何子类替换任何 Property 实例。


仅,在您的示例中在类上声明的泛型类型允许绕过此限制并让子类决定将子类用作参数。

无论如何,它看起来很糟糕且无法维护

这是非常可维护的,因为如果您重构类名,它将被更新。 但这确实有点冗长。

【讨论】:

  • 我知道为什么不允许在参数中使用子类的原因,但是在这种情况下似乎很难绕过它......
  • 所以不要使用继承。
  • 其实你是对的。我真的不需要在基类中指定方法,但是每个子类都应该有它,这似乎是正确的。找到一个实用的解决方法并不是什么大问题,但我想知道是否有办法在技术上做到这一点。谢谢你的回答!
  • 如果您统一操作子类实例,即使用基类型作为声明类型,您要在基类中定义方法。否则,不需要多态性很好,因为它很强大,但因为它很强大,所以对不正确也非常谨慎。当然欢迎你:)
【解决方案2】:

我认为你会使用泛型

public abstract class AbstractBase<T extends Property> {
   public abstract boolean same(T property):
} 
public class Concrete extends AbstractBase<SubProperty> {
   public boolean same(SubProperty property) {
      // TODO 
   } 
} 

【讨论】:

    猜你喜欢
    • 2018-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-26
    • 1970-01-01
    • 2012-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多