Java中的协变返回类型

时间:2012-04-13 02:39:24

标签: java

以下代码使用Java中方法覆盖的概念。

package pkg;

import java.util.ArrayList;
import java.util.List;

abstract class SuperClass
{
    abstract public List<String>getList();
}

final class SubClass extends SuperClass
{
    private List<String>list=null;

    @Override
    public ArrayList<String> getList()
    {
        list=new ArrayList<String>();
        list.add("A");
        list.add("B");
        return (ArrayList<String>) list;
    }
}

final public class Main
{
    public static void main(String[] args)
    {
        SuperClass s=new SubClass();
        List<String>list=s.getList();

        for(String str:list)
        {
            System.out.println(str);
        }
    }
}

按照惯例,方法重写在超类和子类中都使用相同的签名(带有返回类型)。在上面的代码中,getList()SuperClass方法的返回类型为List,其子类中的返回类型为ArrayList。方法覆盖如何在这里工作?

顺便说一下,ArrayList显然是List接口的一个实现,但编译器如何在覆盖getList()方法的同时处理返回类型?

我是否应该相信这样的事情... 被覆盖方法的返回类型被允许是被覆盖方法的返回类型的子类型。

4 个答案:

答案 0 :(得分:20)

在早期的Java中并非如此,但在Java 5.0中已经改变了。

  

同一个类中不能有两个方法,只有返回类型不同的签名。在J2SE 5.0发布之前,类也无法覆盖从超类继承的方法的返回类型。在本技巧中,您将了解J2SE 5.0中允许协变返回类型的新功能。这意味着子类中的方法可以返回一个对象,该对象的类型是由超类中具有相同签名的方法返回的类型的子类。此功能消除了过度类型检查和转换的需要。

互联网上不再提供此信息的来源。

答案 1 :(得分:5)

在面向对象的编程中,方法的协变返回类型可以被更窄的&#34;更小的&#34;替换。在子类中重写方法时键入。一种值得注意的语言是C ++,这是一种相当普遍的范例。 自JDK5.0发布以来,Java语言已经(部分)允许使用协变返回类型,因此以下示例不会在以前的版本中进行编译:

 // Classes used as return types:

 class A {
 }

 class B extends A {
 }

 // "Class B is more narrow than class A"
 // Classes demonstrating method overriding:

 class C {
     A getFoo() {
         return new A();
     }
 }

 class D extends C {
     B getFoo() {
         return new B();
     }
 }

更具体地说,协变(从宽到窄)或逆变(从窄到宽)返回类型是指将覆盖方法的返回类型更改为与(但不同于)返回类型相关的类型的情况。原始的重写方法。

两个协变返回类型之间的关系通常是允许用Liskov替换原则替换另一个类型的关系。

这通常意味着重写方法的返回类型将是重写方法的返回类型的子类型。上面的示例具体说明了这种情况。如果不允许替换,则返回类型是不变的并导致编译错误。

参考:https://en.wikipedia.org/wiki/Covariant_return_type

答案 2 :(得分:3)

是的,这是正确的。由于ArrayList是List,因此当原始方法返回List时,您可以返回ArrayList。

答案 3 :(得分:0)

我使用 OPs 示例的 javap 命令检查了字节码,发现编译器在 SubClass 类中生成了一个额外的方法:

//1
public java.util.List getList();

调用

//2
public java.util.ArrayList getList();

方法并返回结果。

SubClass sc=new SubClass();
//the ArrayList getList() will be invoked:
sc.getList();

SuperClass s=...;
//the List getList() will be invoked:
s.getList();