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,而是处理类型。
想到它的简单方法是, ArrayList
是List
,但List
不一定是ArrayList
。
ArrayList
实现List
接口,因此可以将其视为List
。 但是,仅仅因为某些东西实现了List
,它就不是ArrayList
。 例如, LinkedList
实现List
,但不是ArrayList
。
例如,允许以下内容:
List arrayList = new ArrayList(); List linkedList = new LinkedList();
这是因为ArrayList
和LinkedList
都实现了List
接口,因此它们都可以作为List
来处理。
但是,不允许以下内容:
ArrayList arrayList = new LinkedList();
尽管ArrayList
和LinkedList
实现了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
另一种看待它的方法是“赋值兼容性”。 语义上,方法调用
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
类型。
通常,如果T
是U
的子类型(包括T
和U
相同的情况),则类型T
对于类型U
是“可分配的”。
因此, input
的类型必须是List
或List
的子类型(例如, ArrayList
或LinkedList
)。 类似地, output
类型必须是List
或List
的超类型 (例如, Collection
或甚至Object
)。