【问题标题】:Java: Overriding an abstract method in subclassJava:覆盖子类中的抽象方法
【发布时间】:2012-07-19 21:45:40
【问题描述】:

我真的应该知道这一点,但由于某种原因我不明白以下内容。

我的抽象类包含以下抽象方法:

protected abstract RuleDTO createRowToBeCloned(RuleDTO ruleDTO);

我还有一个类如下:

EvaluationRuleDTO extends from RuleDTO

然后在我的抽象类的子类中,由于“必须重写或实现超类型方法”,我有以下不允许的实现:

protected EvaluationRuleDTO createRowToBeCloned(EvaluationRuleDTO ruleDTO) {

但是,以下是允许的:

protected EvaluationRuleDTO createRowToBeCloned(RuleDTO ruleDTO) {

我意识到这可能是一个基本问题,但我有点困惑。为什么我可以在被覆盖的方法中返回RuleDTO的子类,却不能传入子类?

谢谢

【问题讨论】:

    标签: java oop


    【解决方案1】:

    您违反了 Liskov 原则:超类可以做的所有事情,子类必须可以做。超类声明了一个接受任何类型 RuleDTO 的方法。但是在您的子类中,您只接受 EvaluationRuleDTO 的实例。如果您执行以下操作会发生什么?

    RuleDTO rule = new EvaluationRuleDTO();
    rule.createRowToBeCloned(new RuleDTO());
    

    EvaluationRuleDTO 是 RuleDTO,因此它必须履行 RuleDTO 定义的合同。

    但是,子类中的方法可能会返回 EvaluationRuleDTO 的实例而不是 RuleDTO,因为约定是返回 RuleDTO,而 EvaluationRuleDTO 是 RuleDTO。

    【讨论】:

    • 这正是我想要的。谢谢!
    • "子类中的方法可能会返回一个EvaluationRuleDTO 的实例而不是一个RuleDTO" => 我在这里没有看到任何问题,这实际上在Java 中是允许的。 (也许它不是在 2012 年?)这将编译得很好:pastebin.com/Vr4xQPqk。你几乎总是有很好的答案,但我根本不明白这个。
    • @KorayTugay 我也没有看到任何问题。这就是这句话的重点:说明子类中的方法可以返回更具体的东西。
    【解决方案2】:

    Java 允许覆盖的返回类型协变,因此您可以将覆盖的返回类型指定为更多派生类型。但是,Java 不允许覆盖的参数类型协变。

    前者安全的原因是您返回的对象将至少具有较少派生类型的功能,因此依赖该事实的客户端仍然能够正确使用返回的对象。

    不过,论点并非如此。如果它是合法的,用户可以调用抽象方法并传入一个派生较少的类型(因为这是在抽象类上声明的类型),但是你的派生重写可能会尝试将参数作为派生更多的类型来访问(它不是)导致错误。

    理论上,Java 可以允许参数类型 contra-variance,因为这是类型安全的:如果被覆盖的方法只需要 less 派生参数,您不能意外使用不存在的方法或字段。不幸的是,这目前不可用。

    【讨论】:

      【解决方案3】:

      这是因为当你重写一个方法时,你必须使用相同类型或更通用(更广泛)的类型作为参数类型,而不是更窄的类型。

      想想吧。如果你可以使用更窄的类型覆盖一个方法,你会破坏多态能力,你不同意吗?所以,这样做,你会破坏Liskov Substitution Principle,正如 JB Nizet 在他的回答中所说的那样。

      【讨论】:

      • 从您的回答中引用“您必须使用相同类型或更通用(更广泛)的类型,而不是更窄的类型”作为参数类型,它不适用于 Java。尽管从 Liskov 原理的角度来看确实如此,但方法参数类型在 Java 中必须是不变的。
      【解决方案4】:

      Java 1.5 有 co-variant return 类型,这说明它是有效的

       

      子类方法的返回类型 R2 可能与父类不同 方法的返回类型 R1,但 R2 应该是 R1 的子类型。 IE。, 子类可以返回类型可能是超类返回类型的子类型。

      【讨论】:

        【解决方案5】:

        在早期的 Java 中并非如此,但在 Java 5.0 中发生了变化。

        您不能在同一个类中拥有两个方法,其签名仅在返回类型上有所不同。在 J2SE 5.0 发布之前,一个类也确实不能覆盖它从超类继承的方法的返回类型。在本技巧中,您将了解 J2SE 5.0 中允许协变返回类型的新特性。这意味着子类中的方法可以返回一个对象,该对象的类型是该方法返回的类型的子类,在超类中具有相同的签名。此功能无需过多的类型检查和强制转换。

        来源:http://www.java-tips.org/java-se-tips/java.lang/covariant-return-types.html

        这意味着覆盖方法的返回类型将是被覆盖方法的返回类型的子类型。

        【讨论】:

          【解决方案6】:

          请看以下代码:

          class A {
              A foo(A a) {
                  return new A();
              }
          }
          
          class B extends A {
              @Override
              // Returning a subtype in the overriding method is fine,
              // but using a subtype in the argument list is NOT fine!
              B foo(B b) {
                  b.bar();
                  return new B();
              }
              void bar() {
                  // B specific method!
              }
          }
          

          是的,好的,B 是 A,但是如果有人这样做会发生什么:

          B b = new B();
          b.foo(new A());
          

          A 没有 bar 方法。这就是为什么参数不能是被覆盖方法中参数类型的子类型。

          在覆盖方法中返回 A 或 B 很好。下面的 sn-p 将编译并运行得很好..

          class A {
              A foo(A a) {
                  return new B(); // B IS AN A so I can return B!
              }
          }
          
          class B extends A {
              @Override
              B foo(A b) {
                  return new B(); // Overridden method returns A and 
                                  // B IS AN A so I can return B!
              }
          
              public static void main(String[] args) {
                  A b = new B();
                  final A foo = b.foo(new B());
                  // I can even cast foo to B!
                  B cast = (B) foo;
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2022-08-18
            • 2011-02-25
            • 1970-01-01
            • 1970-01-01
            • 2015-08-20
            • 1970-01-01
            • 2016-01-02
            相关资源
            最近更新 更多