165

被覆盖的方法可以有不同的返回类型吗?

4

12 回答 12

205

Java 支持重写方法的协变返回类型这意味着被覆盖的方法可能具有具体的返回类型。也就是说,只要新的返回类型可以分配给您要覆盖的方法的返回类型,它就被允许。

例如:

class ShapeBuilder {
    ...
    public Shape build() {
    ....
}

class CircleBuilder extends ShapeBuilder{
    ...
    @Override
    public Circle build() {
    ....
}

在 Java 语言规范的第 8.4.5 节中指定:

如果返回类型是引用类型,则返回类型可能会因相互覆盖的方法而异。return-type-substitutability 的概念支持协变返回,即将返回类型特化为子类型。

当且仅当以下条件成立时,具有返回类型 R1 的方法声明 d1 可以返回类型替代具有返回类型 R2 的另一个方法 d2:

  • 如果 R1 无效,则 R2 无效。

  • 如果 R1 是原始类型,则 R2 与 R1 相同。

  • 如果 R1 是引用类型,则:

    • R1 是 R2 的子类型,或者 R1 可以通过未经检查的转换(第 5.1.9 节)转换为 R2 的子类型,或者

    • R1 = |R2|

(“|R2|”指的是 R2 的擦除,如JLS 的§4.6 中所定义。)


* 在 Java 5 之前,Java 具有不变的返回类型,这意味着方法覆盖的返回类型需要与被覆盖的方法完全匹配。

于 2013-02-04T20:18:47.020 回答
36

是的,它可能会有所不同,但有一些限制。

在 Java 5.0 之前,当您覆盖一个方法时,参数和返回类型必须完全匹配。Java 5.0 它引入了一种称为协变返回类型的新工具。您可以覆盖具有相同签名的方法,但返回返回对象的子类。

换句话说,子类中的方法可以返回一个对象,该对象的类型是该方法返回的类型的子类,在超类中具有相同的签名。

于 2013-12-11T09:02:14.300 回答
21

是的,如果他们返回一个子类型。这是一个例子:

package com.sandbox;

public class Sandbox {

    private static class Parent {
        public ParentReturnType run() {
            return new ParentReturnType();
        }
    }

    private static class ParentReturnType {

    }

    private static class Child extends Parent {
        @Override
        public ChildReturnType run() {
            return new ChildReturnType();
        }
    }

    private static class ChildReturnType extends ParentReturnType {
    }
}

此代码编译并运行。

于 2013-02-04T20:20:57.467 回答
12

从广义上讲,是的,覆盖方法的返回类型可以不同。但这并不是直截了当的,因为其中涉及一些案例。

情况 1:如果返回类型是原始数据类型或 void。

输出:如果返回类型为 void 或原始类型,则父类方法和覆盖方法的数据类型应该相同。例如,如果返回类型是 int、float、string 那么它应该是相同的

情况2:如果返回类型是派生数据类型:

输出:如果父类方法的返回类型是派生类型,那么覆盖方法的返回类型是与派生数据类型相同的子类的派生数据类型。例如,假设我有一个类 A,B 是 A 的子类,C 是 B 的子类,D 是 C 的子类;那么如果超类返回类型 A,那么子类中的覆盖方法可以返回 A 或 B/C/D 类型,即它的子类型。这也称为协方差。

于 2016-06-10T03:26:47.497 回答
5


是有可能..只有当父类方法返回类型是子类方法返回类型的超类型时,返回类型才能不同..
意味着

class ParentClass {
    public Circle() method1() {
        return new Cirlce();
    }
}

class ChildClass extends ParentClass {
    public Square method1() {
        return new Square();
    }
}

Class Circle {

}

class Square extends Circle {

}


如果这是那么可以允许不同的返回类型......

于 2016-03-01T17:44:11.943 回答
4

其他答案都是正确的,但令人惊讶的是,这里都忽略了理论方面:返回类型可以不同,但​​由于Liskov Substitution Principle,它们只能限制超类中使用的类型。

这非常简单:当您有调用某个方法的“客户端”代码时:

int foo = someBar.bar();

然后上面的内容必须起作用(并且int无论bar()调用哪个实现都返回一些东西)。

含义:如果有一个 Bar 子类覆盖,bar()那么您仍然必须返回不会破坏“调用者代码”的东西。

换句话说:假设基础bar()应该返回 int。然后一个子类可以返回short- 但不是long因为调用者可以很好地处理一个short值,但不是long

于 2018-03-16T13:25:27.990 回答
2

好吧,答案是肯定的……而且不是。

取决于问题。这里的每个人都回答了关于 Java >= 5 的问题,有些人提到 Java < 5 不具有协变返回类型。

实际上,Java 语言规范 >= 5 支持它,但 Java 运行时不支持。特别是,JVM 没有更新以支持协变返回类型。

在当时被视为“聪明”的举动,但最终成为 Java 历史上最糟糕的设计决策之一,Java 5 实现了一系列新的语言特性,而根本没有修改 JVM 或类文件规范。相反,所有功能都是在 javac 中用诡计实现的:编译器为嵌套/内部类生成/使用普通类,为泛型生成/使用普通类,为泛型生成/使用类型擦除和强制转换,为嵌套/内部类私有“友谊”合成访问器,为外部“this”合成实例字段指针、“.class”字面量的合成静态字段等。

协变返回类型是 javac 添加的更多语法糖。

例如,在编译时:

class Base {
  Object get() { return null; }
}

class Derived extends Base {
  @Override
  @SomeAnnotation
  Integer get() { return null; }
}

javac 会在 Derived 类中输出两个 get 方法:

Integer Integer:Derived:get() { return null; }
synthetic bridge Object Object:Derived:get() { return Integer:Derived:get(); }

生成的桥接方法(标记syntheticbridge字节码)实际上是覆盖Object:Base:get()的,因为对于 JVM,具有不同返回类型的方法是完全独立的,不能相互覆盖。为了提供预期的行为,桥简单地调用你的“真实”方法。在上面的示例中,javac 将使用 @SomeAnnotation 注释 Derived 中的桥接方法和真实方法。

请注意,您不能在 Java < 5 中手动编写此解决方案,因为桥方法和实方法仅在返回类型上有所不同,因此它们不能在 Java 程序中共存。但是在 JVM 世界中,方法返回类型是方法签名的一部分(就像它们的参数一样),因此两个名称相同并采用相同参数的方法仍然被 JVM 视为完全独立,因为它们的返回类型不同,并且可以共存。

(顺便说一句,字段的类型同样是字节码中字段签名的一部分,因此在单个字节码类中具有多个不同类型但名称相同的字段是合法的。)

所以要完全回答你的问题:JVM 不支持协变返回类型,但 javac >= 5 在编译时用甜美的语法糖涂层伪造它。

于 2019-04-02T08:52:44.840 回答
1

返回类型必须与超类的原始重写方法中声明的返回类型相同,或者是其子类型。

于 2013-02-04T20:36:26.997 回答
1

覆盖和返回类型,以及协变返回
子类必须定义一个与继承版本完全匹配的方法。或者,从 Java 5 开始,您可以在

示例代码


                                                                                                            class Alpha {
          Alpha doStuff(char c) {
                  return new Alpha();
              }
           }
             class Beta extends Alpha {
                    Beta doStuff(char c) { // legal override in Java 1.5
                    return new Beta();
                    }
             } } 
Java 5,此代码将编译。如果您尝试使用 1.4 编译器编译此代码,则会说尝试使用不兼容的返回类型 – sandeep1987 1 分钟前

于 2013-02-04T21:00:47.687 回答
0

是的,有可能

class base {

 base show(){

System.out.println("base class");

return new base();

}
}

class sub extends base{

sub show(){

    System.out.println("sub class");

    return new sub();

 }
}

class inheritance{

 public static void main(String []args) {

        sub obj=new sub();

            obj.show();
 }
}
于 2014-07-23T20:09:47.240 回答
0

是的。被覆盖的方法可能有不同的返回类型。

但是限制是被覆盖的方法必须有一个比实际方法的返回类型更具体的返回类型。

所有答案都给出了重写方法的示例,该方法具有返回类型,该返回类型是实际方法的返回类型的子类。

例如 :

public class Foo{

   //method which returns Foo
  Foo getFoo(){
      //your code         
  }

}

 public class subFoo extends Foo{

  //Overridden method which returns subclass of Foo
  @Override
  subFoo getFoo(){
      //your code         
  }

}

但这不仅限于子类。即使实现接口的类也是接口的特定类型,因此可以是预期接口的返回类型。

例如 :

public interface Foo{

   //method which returns Foo
  Foo getFoo();

}

 public class Fizz implements Foo{

  //Overridden method which returns Fizz(as it implements Foo)
  @Override
  Fizz getFoo(){
      //your code         
  }

}
于 2019-01-05T13:47:05.827 回答
0
class Phone {
    public Phone getMsg() {
        System.out.println("phone...");
        return new Phone();
    }
}

class Samsung extends Phone{
    @Override
    public Samsung getMsg() {
        System.out.println("samsung...");
        return new Samsung();
    }
    
    public static void main(String[] args) {
        Phone p=new Samsung();
        p.getMsg();
    }
}
于 2020-07-12T07:24:46.680 回答