Java多态性混乱

时间:2009-05-13 15:31:02

标签: java types polymorphism

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

的方法
public static <E extends Number> List<E> process(List<E> nums)

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

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

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

一个。

ArrayList<Integer> input = null;
ArrayList<Integer> output = null;

ArrayList<Integer> input = null;
List<Integer> output = null;

下进行。

ArrayList<Integer> input = null;
List<Number> output = null;

d

List<Number> input = null;
ArrayList<Integer> output = null;

电子。

List<Number> input = null;
List<Number> output = null;

F。

List<Integer> input = null;
List<Integer> output = null;

-G。以上都不是。

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

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

由于

8 个答案:

答案 0 :(得分:13)

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

答案 1 :(得分:3)

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

答案 2 :(得分:3)

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

简单的方法是,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的方法具有相似性,但它们是完全独立的类。

答案 3 :(得分:3)

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

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

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

答案 4 :(得分:1)

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

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

答案 5 :(得分:1)

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

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

......这与仿制药允许你完成的事情相反。

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

output = process(input)

相当于

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

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

通常,如果TU的子类型,T类型可以“分配”到U类型(包括T的情况和U是一样的)。

因此,input的类型必须为List<E>List<E>的子类型(例如ArrayList<E>LinkedList<E>)。同样,output的类型必须为List<E>List<E>超类型(例如,Collection<E>或甚至Object)。