【问题标题】:Why java does not allow to change return type in case of method overriding为什么java不允许在方法覆盖的情况下更改返回类型
【发布时间】:2016-06-23 13:20:48
【问题描述】:

我有 A 类:

 public class A {
    int sayHello(int i,int j){
        return i+j;
    }
 }

另一个B类为:

public class B extends A {  
    @Override
    int sayHello(int i, int j) {
        return i+j;
    }
}

如果我将 A 类的 sayHello(int,int) 方法的返回类型从 int 更改为 float,则会显示错误,因为根据覆盖规则,返回类型也被认为是为什么它也不是有效的覆盖和重载。

我对 为什么 java 不允许更改返回类型感到困惑。为什么返回类型也需要相同

【问题讨论】:

  • 允许多态性将是创建难以检测的错误的一个很好的原因。那么像int blub = myInterfaceType.getIntFromImplementedType() 这样的东西就不安全了,因为它可以返回任何东西,而不仅仅是int

标签: java


【解决方案1】:

因为:

class Parent {
    int method() { return 1; }
}

class Child extends Parent {
    float method() { return 1.0f; }
}

Parent p = new Child();
int myInt = p.method();

method() 返回什么?它应该是一个int,因为Parent 说它是一个int。但是Child 返回一个float。那么你期望会发生什么? JVM 应该崩溃吗?

顺便说一句,您可以更改返回类型,但它必须是类型的子类型,被覆盖的方法返回。所以你可以覆盖一个返回Parent的方法,并使用Child作为返回类型。

【讨论】:

    【解决方案2】:

    可以在你重写方法时改变返回类型:它只需要被一个协变类型重写。

    例如,如果您定义如下接口:

    interface Foo {
      Object foo();
    }
    

    那么就可以这样实现了:

    class Bar implements Foo {
      @Override public String foo() {
        return "";
      }
    }
    

    因为Foo.foo() 的所有实现都必须返回Object,并且所有Strings 都是Objects。

    但是,你不能反过来做:如果Foo.foo()的返回类型是String,你不能实现它来返回一个Object,因为方法的调用者需要是能够在该实例上调用String 的方法。例如:

    void test(Foo instance) {
      System.out.println(instance.foo().length());
    }
    

    如果返回 Object,这在运行时将不起作用,因为在 Object 上没有 length() 方法。 (但更具体地说,它必须是 String.length() 方法,而不仅仅是 any 长度方法:Java 不支持鸭子类型)。编译器可以检测到这种不匹配,所以它会阻止你这样做。

    没有与int(或任何原始类型,就此而言)协变的类型,因此当您覆盖返回int 的方法时,您必须返回int


    这是允许的理论上的原因是Liskov Substitution Principle,可以解释为“子类型方法可以在它们接受的参数上更通用,在它们返回的类型上更具体”。

    Java 不允许使用更通用的参数类型,但是,因为它解决方法重载的方式。

    【讨论】:

      【解决方案3】:

      如图here(强调我自己的)

      子类重写方法的能力允许类 从行为“足够接近”的超类继承,然后 根据需要修改行为。 覆盖方法同名, 参数的数量和类型,返回类型作为它的方法 覆盖。 覆盖方法还可以返回该类型的子类型 由重写的方法返回。这种子类型称为协变 返回类型。

      如果返回类型不一样,它会破坏其他可以使用多态性在运行时检测类的类型的类。

      【讨论】:

        【解决方案4】:

        基本上,Java 认为这个 (非常正确,我认为 ...) 是一个讨厌的错误(!),它只是在编译时为您提供了帮助。

        后代类和覆盖背后的基本思想是,调用者不必关心他们可能正在处理的特定类实例的“风格”,只要知道它 is an 的实例该类或其后代之一。编译器还可以生成可靠的代码,因为尽管特定类的底层实现可能会有所不同,但原型不会:每个实例都有相同的方法, a-n-d, 将返回相同的数据类型。

        如果您需要在特殊情况下返回不同的数据类型,您应该定义一个自定义方法来执行此操作。这也将(!)对于将要关注您的各种程序员很重要。 (“那辆面包车太糟糕了......”等等) 如果你的代码有些不同,它应该非常明显看起来不同 在应用程序源代码中!

        请记住:如果 Java 真的允许您做这种事情,您可能会在任何现有的“调试”代码中,在任何地方无意中引入 nas-s-s-s-ssty 错误 (!) 在您无疑是庞大的应用程序中,碰巧偶然发现了“您的特殊情况”的一个实例。

        【讨论】:

          猜你喜欢
          • 2021-11-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-04-14
          • 2010-12-10
          • 2014-11-03
          相关资源
          最近更新 更多