关于java:可以覆盖的方法在返回类型上有所不同吗?

Can overridden methods differ in return type?

重写的方法可以有不同的返回类型吗?


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

例如:

1
2
3
4
5
6
7
8
9
10
11
12
class ShapeBuilder {
    ...
    public Shape build() {
    ....
}

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

这是在Java语言规范的第8章4.5节中指定的:

Return types may vary among methods that override each other if the return types are reference types. The notion of return-type-substitutability supports covariant returns, that is, the specialization of the return type to a subtype.

A method declaration d1 with return type R1 is return-type-substitutable for another method d2 with return type R2, if and only if the following conditions hold:

  • If R1 is void then R2 is void.

  • If R1 is a primitive type, then R2 is identical to R1.

  • If R1 is a reference type then:

    • R1 is either a subtype of R2 or R1 can be converted to a subtype of R2 by unchecked conversion (§5.1.9), or

    • R1 = |R2|

("r2"是指删除r2,如jls§4.6所定义。)

*在Java 5之前,Java具有不变的返回类型,这意味着需要完全重写被重写的方法所需的方法重写的返回类型。


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

在Java 5之前,当重写方法时,两个参数和返回类型必须完全匹配。在Java 5中,它引入了一种称为协变返回类型的新工具。可以用相同的签名重写方法,但返回返回的对象的子类。换句话说,子类中的方法可以返回一个对象,该对象的类型是该方法返回的类型的子类,该方法在超类中具有相同的签名。


是的,如果它们返回子类型。下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 {
    }
}

此代码编译并运行。


一般来说,是的,重写方法的返回类型可以不同。但这并不是一帆风顺的,这其中涉及到一些案例。

案例1:如果返回类型为基元数据类型或void。

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

案例2:如果返回类型是派生数据类型:

输出:如果父类方法的返回类型是派生类型,则重写方法的返回类型与派生数据类型的子类的派生数据类型相同。例如,假设我有A班B是A的子类C是B的子类D是C的子类如果超级类返回类型A,则重写方法是Subclass可以返回A、B、C或D类型,即其子类型。这也被称为协变。


是的,有可能……只有父类方法返回类型为时,返回类型才能不同子类方法的超类型返回类型..方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
            class ParentClass {
                  public Circle() method1() {
                    return new Cirlce();
                  }
            }

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


            Class Cirlce {

            }

            class Square extends Circle {

            }

        ---

如果这是,则可以允许不同的返回类型…


重写和返回类型,以及协变返回
子类必须定义与继承版本完全匹配的方法。或者,如Java 5所示,您可以在
示例代码中更改返回类型。

1
2
3
4
5
6
7
8
9
10
                                                                                                            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分钟前


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


是的,这是可能的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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();
 }
}

嗯,答案是肯定的…NO.

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

实际上,Java语言SPEC>=5支持它,但Java运行时不支持。尤其是,没有更新JVM来支持协变返回类型。

在后来被看作是一种"聪明"的动作,但最终成为Java历史上最糟糕的设计决定之一时,Java 5实现了一组新的语言特征,而不需要修改JVM或类文件规格。相反,所有特性都是用javac中的技巧实现的:编译器为嵌套/内部类生成/使用普通类,为泛型生成/使用类型擦除和强制转换,为嵌套/内部类私有"友谊"生成合成访问器,为外部"this"指针生成合成实例字段,为".class"文本生成合成静态字段等。

而协变返回类型则是javac添加的更多语法成分。

例如,在编译此文件时:

1
2
3
4
5
6
7
8
9
class Base {
  Object get() { return null; }
}

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

javac将在派生类中输出两个get方法:

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

生成的桥接方法(字节码中标记为syntheticbridge)实际上是重写Object:Base:get()的方法,因为对于JVM来说,具有不同返回类型的方法是完全独立的,不能互相重写。为了提供预期的行为,桥接器只调用您的"real"方法。在上面的示例中,javac将用@someannotation对派生的bridge和real方法进行注释。

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

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

因此,要完全回答您的问题:JVM不支持协变返回类型,但是javac>=5在编译时用一层甜甜的语法糖来伪造它。


是的,我们可以!协变返回类型是一个常见的例子


对。重写的方法可能具有不同的返回类型。

但限制是重写方法必须具有返回类型,该返回类型是实际方法返回类型的更具体类型。

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

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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        
  }

}

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

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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        
  }

}


其他的答案都是正确的,但令人惊讶的是,这里忽略了理论方面的内容:返回类型可以是不同的,但由于liskov替换原则,它们只能限制在超级类中使用的类型。

这非常简单:当您有"客户机"代码调用某些方法时:

1
int foo = someBar.bar();

然后,上述操作必须有效(并返回一个int的东西,无论调用哪个bar()的实现)。

意思是:如果有一个bar子类覆盖bar(),那么您仍然需要返回一些不破坏"调用者代码"的内容。

换言之:假设基础bar()应该返回int,那么子类可以返回short,但不能返回long,因为调用方可以处理short值,而不能处理long