Java多态性混乱

下面的问题来自Kathy Sierra和Bert Bates的Java SCJP5一书。 给定一个声明为的方法:

public static  List process(List nums) 

程序员想要使用这样的方法:

 // INSERT DECLARATIONS HERE output = process(input); 

可以在// INSERT DECLARATIONS这里放置哪一对声明以允许代码编译? (选择所有适用的选项。)

一个。

 ArrayList input = null; ArrayList output = null; 

B.

 ArrayList input = null; List output = null; 

C。

 ArrayList input = null; List output = null; 

D.

 List input = null; ArrayList output = null; 

E.

 List input = null; List output = null; 

F。

 List input = null; List output = null; 

G.以上都不是。

给出的正确答案是:B,E,F和书中的解释说明:
“返回类型肯定被声明为List,而不是ArrayList,因此A,D是错误的。……”

这是我没有得到的……为什么返回类型必须只是List而不是ArrayList? 就像参数可以是ArrayList那么为什么cant返回类型也是arrayList?

谢谢

这实际上并不特定于generics,而是处理类型。

想到它的简单方法是, ArrayListList ,但List不一定是ArrayList

ArrayList实现List接口,因此可以将其视为List 。 但是,仅仅因为某些东西实现了List ,它就不是ArrayList 。 例如, LinkedList实现List ,但不是ArrayList

例如,允许以下内容:

 List arrayList = new ArrayList(); List linkedList = new LinkedList(); 

这是因为ArrayListLinkedList都实现了List接口,因此它们都可以作为List来处理。

但是,不允许以下内容:

 ArrayList arrayList = new LinkedList(); 

尽管ArrayListLinkedList实现了List ,但它们不是同一个类。 它们可能通过实现List的方法具有相似性,但它们是完全独立的类。

因为ArrayList是List的子类,所以进程返回的List不保证是ArrayList。 例如,它可能是LinkedList。

当您指定List类型的返回时,您会说返回可以是任何类型的List,无论它是ArrayList,LinkedList还是其他类型的List。 如果你试图返回一个ArrayList,那你就太具体了 – 它不再是一个通用的List。

返回类型可以是ArrayList,但它也可以不是ArrayList,它可以是LinkedList,也可以是某些Collections List包装器或其他东西。 为了将它分配给变量,你必须使用它可能的最高级别类型(在这种情况下为List)或者如果你知道它必须是一个ArrayList则是向下转换。

在实践中,变量应该几乎总是键入List(如果不是Collection)。 实际上几乎没有理由为这些类引用具体类型。

我能想到的唯一原因是,如果你有一个方法需要随机访问List,并且你想确保你没有因为性能原因而得到LinkedList而且List太大而无法合理地复制到ArrayList用于此目的随机访问。

您可以将返回类型声明为ArrayList ,但它需要显式转换,如下所示:

 ArrayList output = null; output = (ArrayList)process(input); 

……这与generics允许你完成的事情相反。

process方法无法决定选择List的具体实现。 它的签名给你一个承诺,它能够在任何List(ArrayList,LinkedList等)上运行,并且只能指定返回值是SOME List。

例如,如果返回的对象是LinkedList,则无法将其直接分配给声明为ArrayList的变量,但您可以将其视为List。 因此A.和D.不正确。 您不能将变量声明为ArrayList。

这是因为问题明确表示该方法返回List 。 您可以很好地更改方法定义以返回ArrayList ,但这是一个不同的问题。 List 可以是许多不同类型的列表。

另一种看待它的方法是“赋值兼容性”。 语义上,方法调用

 output = process(input) 

相当于

 nums = input; /* body of process... */ output = returnVal; /* where process exits with "return returnVal" */ 

我们知道nums类型为List ,因此input类型必须是“可分配”到List 。 同样,我们知道returnVal (即process的返回值)类型为List ,因此List必须是“可赋值”的output类型。

通常,如果TU的子类型(包括TU相同的情况),则类型T对于类型U是“可分配的”。

因此, input的类型必须是ListList的子类型(例如, ArrayListLinkedList )。 类似地, output类型必须是ListList超类型 (例如, Collection或甚至Object )。