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

问题描述 投票:115回答:12

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

java methods override
12个回答
158
投票

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

例如:

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

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

这在section 8.4.5 of the Java Language Specification中指定:

如果返回类型是引用类型,则返回类型可能会因覆盖彼此的方法而异。返回类型可替代性的概念支持协变返回,即返回类型到子类型的特化。

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

  • 如果R1无效,则R2无效。
  • 如果R1是基本类型,则R2与R1相同。
  • 如果R1是引用类型,则: R1是R2的子类型,或者R1可以通过未经检查的转换(第5.1.9节)转换为R2的子类型,或者 R1 = | R2 |

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


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


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
投票

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


0
投票

好吧,答案是肯定的......不。

取决于问题。这里的每个人都回答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在编译时伪造它,带有甜的语法糖涂层。


21
投票

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

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


18
投票

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

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

此代码编译并运行。


7
投票

从广义上讲,返回类型的重写方法可能不同。但它并非直截了当,有一些案例涉及到这一点。

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

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

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

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


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 {

}


If this is the then different return type can be allowed...

2
投票

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


2
投票

覆盖和返回类型以及协变返回 子类必须定义一个完全匹配继承版本的方法。或者,从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();
                    }
             } } 
As of Java 5, this code will compile. If you were to attempt to compile this code with a 1.4 compiler will say attempting to use incompatible return type – sandeep1987 1 min ago

1
投票

是的,它是可能的

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

1
投票

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

它非常简单:当你有“客户端”代码调用某些方法时:

int foo = someBar.bar();

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.