Java泛型中的多态
從作為Java程序員的早期開(kāi)始,我們都知道如何實(shí)例化和使用Collection對(duì)象。 實(shí)例化為具體類的List接口將如下所示。
List myArrayList = new ArrayList();如果myArrayList應(yīng)該僅保存Integer對(duì)象,則從Java 5編譯器開(kāi)始,按照J(rèn)ava Generics規(guī)范,實(shí)例化將如下所示:
List<Integer> myArrayList = new ArrayList<Integer>();在同一行中,接受和/或返回字符串列表的方法將從
public List processStrings(ArrayList myStringList);至
public List<String> processStrings(ArrayList<String> myStringList);而且它們是類型安全的,因此我們不必進(jìn)行強(qiáng)制轉(zhuǎn)換即可檢索列表對(duì)象的項(xiàng)目
String aStringFromMyStringList = myStringList.get(0); //No ClassCastException possible.如果將aStringFromMyStringList聲明為String以外的任何內(nèi)容,則以上內(nèi)容將不會(huì)編譯。
到這里為止,我們應(yīng)該對(duì)面向?qū)ο蟮腏ava如何工作感到滿意,但是下一項(xiàng)可能會(huì)讓很多人感到驚訝。
當(dāng)我們使用List<Integer> myArrayList = new ArrayList<Integer>(); 意味著我們應(yīng)該只在ArrayList和NOTHING ELSE中使用“ Integer”。 等一下,泛型不是OOP的一部分,這意味著我們不能在這些對(duì)象中應(yīng)用多態(tài)嗎? 答案是不。 讓我們看看為什么。
我們已經(jīng)看到多態(tài)性適用于集合的基本類型,這就是為什么List<Integer> myArrayList可以實(shí)例化為新的ArrayList<Integer>();
但是呢:
class Parent{}class Child extends Parent{}使用以上方法,以下實(shí)例將無(wú)法正常工作,并最終導(dǎo)致編譯錯(cuò)誤。
List<Parent> myList = new ArrayList<Child>() //Compilation Error;一個(gè)簡(jiǎn)單的規(guī)則是變量聲明的類型必須與您傳遞給實(shí)際對(duì)象類型的類型相匹配。 如果我們聲明List<Parent> myList那么我分配給myList任何myList必須僅是<Parent>類型,而不是Parent類的子類型,而不是Parent類的超類型。
這意味著正確的代碼是:
List<Parent> myList = new ArrayList<Parent>(); // Compiles fine但是以上內(nèi)容與習(xí)慣于使用以下合法內(nèi)容的傳統(tǒng)Java程序員矛盾。
Parent[] myParentArray = new Child[10];要詳細(xì)了解上述差異,讓我們有一個(gè)如下的繼承結(jié)構(gòu):
public class Animal{}public class Cat extends Animal{}public class Dog extends Animal{}我們可以在數(shù)組中實(shí)現(xiàn)多態(tài),因?yàn)椴粦?yīng)將數(shù)組指定為安全類型。 請(qǐng)參見(jiàn)下面的數(shù)組示例,以及為什么我們需要將類型安全列表作為Collection對(duì)象。
public void addAnimals(Animal[] animals ) {animals [0] = new Animal();// If passed animal[] is of type Dog[] then we are adding a Cat object to a Dog[] array.animals [1] = new Cat();// If passed animal[] is of type Cat[] then we are adding a Dog object to a cat[] array.animals [1] = new Dog(); }由于貓或狗是動(dòng)物的類型,因此可以將貓陣列或狗陣列作為動(dòng)物陣列進(jìn)行傳遞。
public class callerClass() {Animal[] animalArray = new Animal[10];Cat[] catArray = new Cat[10];Dog[] dogArray = new Dog[10];addAnimals(animalArray); //Expected, no questions raised here. addAnimals(catArray); //As Cat[] is a type of Animal[] so we may end up in adding a Cat in Dog Array. addAnimals(dogArray); // As Dog[] is a type of Animal[] so if Cat[] is passed we may end up in adding a Dog in a //Cat array. }但是看看如果我們使用Collections會(huì)發(fā)生什么。 我們可以有類似上面的方法:
public void addAnimals(List<Animal> myAnimalList()) { //Some code here. }調(diào)用上述方法的調(diào)用方方法如下所示。
public class callerClass() {List<Animal> animalList = new ArrayList<Animal>();List<Cat> catList = new ArrayList<Cat>();List<Dog> dogList = new ArrayList<Dog>();addAnimals(animalList); addAnimals(catList);addAnimals(dogList); }如果我們嘗試編譯以上內(nèi)容,會(huì)發(fā)生什么? 它將在addAnimals(catList);行失敗addAnimals(catList); 和addAnimals(dogList) ,因?yàn)長(zhǎng)ist類型與addAnimals(List<Animal> myAnimalList())方法的預(yù)期列表類型不匹配。 該方法期望列表僅聲明為動(dòng)物類型。
盡管上面的方法失敗了,但是當(dāng)列表被聲明為超類型列表時(shí),泛型實(shí)際上可以保留子類型的實(shí)例。 例如,我們可以像下面這樣詳細(xì)實(shí)現(xiàn)addAnimals( List<Animal> myAnimalList () myAnimalList List<Animal> myAnimalList () )方法。
public void addAnimals(List<Animal> myAnimalList ()) {aList.add(new Animal()); // Expected code.aList.add(new Cat()); //Yes this works.aList.add(new Dog()); //Any Animal subtype works. }這意味著我們可以將子超類繼承概念應(yīng)用到對(duì)象列表中,而不是將對(duì)象作為方法參數(shù)分配或傳遞給列表。
這就是Java禁止編譯addAnimals(catList)代碼的原因,因?yàn)槿绻幾g了該代碼,則稍后在已實(shí)現(xiàn)的addAnimals方法中,即使aList是一個(gè)aList,也始終可以使用aList.add(new Dog())代碼。貓名單的類型,這是錯(cuò)誤的! 我們不能將Dog對(duì)象添加到Cat列表中,因?yàn)樵摿斜韮H聲明為具有Cat對(duì)象(或其子類)。 泛型可以使列表類型安全并且在技術(shù)上有意義。 為了接受多態(tài)子/超類,我們可以使用通配符來(lái)增強(qiáng)方法簽名,這可以在另一個(gè)會(huì)話中進(jìn)行討論。
翻譯自: https://www.javacodegeeks.com/2015/03/polymorphism-in-java-generics.html
總結(jié)
以上是生活随笔為你收集整理的Java泛型中的多态的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 郭明錤:华为归来对消费者来说是好事,倒逼
- 下一篇: java内存泄漏和内存溢出_Java和内