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


134

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


2
如果您没有相同或更窄的返回类型,则将得到::error: method() in subclass cannot override method() in superclass
Quazi Irfan

Answers:


176

Java支持*协变返回类型的重写方法。这意味着重写的方法可能具有特定的返回类型。也就是说,只要新的返回类型可分配给您要覆盖的方法的返回类型,就可以使用。

例如:

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

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

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

如果返回类型是引用类型,则返回类型在彼此覆盖的方法之间可能会有所不同。return-type-substitability的概念支持协变返回,即,将返回类型专门化为子类型。

当且仅当满足以下条件时,返回类型为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具有不变的返回类型,这意味着方法重写的返回类型必须与被重写的方法完全匹配。


26

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

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


20

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

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 {
    }
}

此代码编译并运行。


9

概括地说,覆盖方法的返回类型可以不同。但这并非一帆风顺,因为其中涉及一些案例。

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

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

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

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


您的评论是模棱两可的,我有AB类是AC的子类,而BD是BD的子类,这意味着AB类是AC或A,B的子类,B是A,C的子类,意味着它令人困惑
dinesh kandpal

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 {

}


如果是这样,则可以允许使用其他返回类型...


2

好吧,答案是肯定的。

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

实际上,> = 5的Java语言规范支持它,但是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中的bridge和real方法。

请注意,您无法在Java <5中手动编码该解决方案,因为bridge和real方法仅在返回类型上有所不同,因此它们不能共存于Java程序中。但是在JVM领域,方法返回类型是方法签名的一部分(就像它们的参数一样),因此由于相同的返回类型不同,JVM认为这两个名称相同且采用相同参数的方法是完全独立的,并且可以共存。

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

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


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分钟前


1

其他答案都是正确的,但令人惊讶的是,这里所有的答案都没有涉及理论方面:返回类型可以不同,但​​是由于Liskov替换原理,它们只能限制超类中使用的类型。

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

int foo = someBar.bar();

那么上面的代码必须工作(并返回intbar()调用哪种实现无关的东西)。

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

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


0

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


5
您应该结合两个答案。
theblang

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();
 }
}

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         
  }

}

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();
    }
}

这如何回答这个问题?
商标的曼彻斯特

这是不同返回类型的示例。
Parth
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.