Java中的协变返回类型

9
下面的代码使用了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);
        }
    }
}

按照惯例,方法重写在超类和子类中使用相同的签名(带返回类型)。在上述代码中,SuperClass 中的 getList() 方法的返回类型为 List,而在其子类中返回类型为 ArrayList。那么这里的方法重写是如何工作的呢?
顺便提一下,显然 ArrayListList 接口的一个实现,但是编译器在重写 getList() 方法时如何处理返回类型呢?
我应该相信这样的说法吗...“被重写方法的返回类型允许是被重写方法返回类型的子类型。”

似乎没有充分的理由将SubClass.list声明为List(而不是ArrayList)。 - Michael Brewer-Davis
@MichaelBrewer-Davis - 当您直接使用SubClass而不是将其视为SuperClass时,这可能会使事情更加美好。 - Brendan Long
@Brendan - 我谈论的是(私有)成员变量过于通用,而不是方法过于特定。方法中的协变是阳光和蝴蝶。 - Michael Brewer-Davis
2
经过仔细检查,似乎没有任何好的理由让 SubClass.list 存在。 - emory
4个回答

20

可以。

在早期的 Java 中,不是这样的,但在 Java 5.0 中进行了更改。

在同一个类中,不能有两个方法的签名仅通过返回类型而不同。在 J2SE 5.0 发布之前,一个类也不能覆盖它从超类继承的方法的返回类型。在本提示中,您将了解 J2SE 5.0 中的一项新功能,即允许协变返回类型。这意味着子类中的方法可以返回其类型为超类中具有相同签名的方法返回类型的子类的对象。该功能消除了对过多类型检查和强制转换的需求。

此信息的来源已经无法在互联网上获得。


1
链接已失效。 - Ghos3t

5
在面向对象编程中,方法的协变返回类型是指当在子类中重写方法时,可以将其替换为“更窄”的类型。这是一种常见的编程范式,其中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();
     }
 }

更具体地说,协变(从宽到窄)或逆变(从窄到宽)返回类型是指重写方法的返回类型已更改为与原始被重写方法的返回类型相关但不同的类型。两个协变返回类型之间的关系通常允许用其中一个类型替换另一个类型,遵循里氏代换原则。这通常意味着重写方法的返回类型将是被重写方法返回类型的子类型。上面的示例明确说明了这种情况。如果不允许替换,则返回类型是不变的,并且会导致编译错误。
参考文献:https://en.wikipedia.org/wiki/Covariant_return_type

3

是的,那是正确的。由于ArrayList是一个List,所以在原始方法返回List时,你可以返回一个ArrayList。


0

我使用javap命令检查了OP示例的字节码,并发现编译器在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();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接