您好,登錄后才能下訂單哦!
本篇內容介紹了“怎么使用Java泛型”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
一般定義如下,即方法的前面加了個<T>
public class FTest { public <T> List<T> f(T t){...}; }
三種泛型參數推斷方式:
1、直接在f()前面加確定泛型
fTest.<Integer>f(xxx)
2、通過輸入參數確定, 下面這個推斷為Integer
int number = 0; fTest.f(number)
3、可通過 返回值 確定
List<Integer> list = fTest.f(xxx);
Q: 下面這段代碼哪里有問題? 是toString()那里嗎?
public class A<T> { public static void test(T t){ System.out.println(t.toString()); } }
A:test是static方法, 因此無法感知A<T>實例里的T
需要改成
public static <T> void test(T t)
toString()那里沒問題,toString就是Object的方法。
Q: 泛型參數T在運行時,會變成什么?
A: 統一變成Object且不包含任何類型信息。
Q: 泛型參數T可以可以使用instanceof做比較嗎?
class A<T> { void f(Object arg) if(arg instanceof T) { ... } }
A: 不能,編譯器會報錯。
Q: 泛型參數T可以進行new T()或者new T[]操作嗎?
A: 不能,編譯器會報錯。
Q: 能調用泛型參數對象里的方法嗎?
T.f();
A: 只能調用Object的方法。
Q: 可以用T做強制轉化嗎?
T t = (T)object;
A: 能運行, 但不會真正發生轉型, 編譯時會觸發waring警告。
先假定有2個類, 基類Parent 和子類Child
class Parent{} class Child extends Parent{}
回答以下問題:
Q:下面這句話有問題嗎?
List<Parent> list = new ArrayList<Child>()
A:有問題,編譯就錯誤了。 List<Parent>和ArrayList<Child>并不存在父子類的關系
Q:
List<? extends Parent> list = new ArrayList<Child>();
這個list有什么特點?
A:這個list可以調用A a = list.get(), 但是不能list.add(new Parent())
原因:
list.get()所做的操作是在返回時, 把內部的<? extend Parent> 強轉成Parent, 是合理的,任何Parent的子類都可以轉成Parent
list.add(new Parent())所做的操作是在輸入時, 把外部的A轉成內部的<? extend Parent>, 這是不合理的,因為我們不知道這個Parent對象可以轉成哪個Parent的子類。
Q:
List<? super Child> list = new ArrayList<Parent>();
這個list有什么特點?
下面誰會報錯
list.add(new Child()) list.add(new Parent()) Parent a= list.get(); Child b = list.get()
A:截圖如下:
Child c = list.get() 或者Parent p = list.get()所做的操作是在返回時, 把內部的<? super Child> 強轉成外部的Parent或者child, 是不合理的, 因為編譯器覺得child的父類 不一定 能轉成parent或者child,所以禁止了這種行為( 比如parent的父類是object, 但object不一定就能轉成parent或者child)。*list.add(new Child())所做的操作是在輸入時, 把外部的child或者parent轉成內部的<? super Child>, 這是合理的,因為child和parent一定能轉成child的父類。
Q:
List<?> list = new ArrayList<A>();
這個list有什么特點?
A:get和add都不行,只能做remove等無返回值無輸入A的操作。
PS: 注意,不是說不能調用get或add方法, 而是調用get或add時,不能使用A這個對象去操作。
即無法做add(A) 或者 A a = get(0)
但是可以做add(object) 或者Object o = get(0)
因為?可以轉為Object, 但是無法轉為A。
Q:下面這個代碼會報錯嗎?
List<Fruit> fruitList = new ArrayList<>(); fruitList.add(new Fruit()); List<Apple> appleList = new ArrayList<>(); appleList.add(new Apple()); fruitList.addAll(appleList); System.out.println(fruitList);
A:不會報錯。會正常打印結果。
PECS原則
注意PECS原則和上面的區別!
上面之前提到的? extend或者? supert, 都是在聲明對象的時候用的。
而PECS原則是用于泛型對象的方法輸入參數!
假設有一個類定義如下:
public static class MyList<T> { List<T> list = new ArrayList<>(); // 把輸入參數塞給自己,類似于生產操作 public void pushList(List<T> t) { list.addAll(t); } // 把自己的內容塞給輸入參數,類似于讓輸入參數做消費。 public void pollList(List<T> t) { t.addAll(list); } }
則T就是泛型參數。
Q:下面代碼能正常運行嗎?
MyList<Number> myList = new MyList<>(); List<Integer> intList = new ArrayList<>(); myList.pushList(intList); List<Object> objectList = new ArrayList<>(); myList.pollList(objectList);
A:不能正常運行, pushList和pollList都會報錯
因為編譯器檢查后,認為 List<Integer>和List<Number>不是一個東西!
Q: 如果上文要支持pushList,應該怎么修改pushList方法的定義?
A:改成這樣:
// 把輸入參數塞給自己,類似于生產操作 public void pushList(List<? extends T> t) { list.addAll(t); }
即編譯器認為,List<Integer> 和List<? extend Number>是一個東西,允許!
Q: 如果要支持pollList,怎么修改定義?
A:
// 把自己的內容塞給輸入參數,類似于讓輸入參數做消費。 public void pollList(List<? super T> t) { t.addAll(list); }
因為是把自己的東西塞給輸入參數, 而想要能塞進去,必須保證自己這個T,是輸入參數的子類,反過來說,輸入參數必須是T的父類,所以用super
于是編譯器認為,List<Object> 和List<? super Number>是一個東西,允許!
PECS原則出自Effective Java, 注意只是一個編程建議而已!
如果有一個類A,泛型參數為T
如果他一般只用于接收輸入容器List后,塞入自己內部的T容器, 則類A就叫生產者, 因此輸入參數最好定義為<? extend T>最好, 以便能接收任何T子類的容器。
如果他一般只用于接收輸入容器后List, 把自己內部的T元素塞給它, 那么這個類A就叫消費者, 輸入參數最好定義為<? super T>\ 最好, 以便自己的T元素能塞給任何T元素的父類容器。
“怎么使用Java泛型”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。