Java泛型定義與用法實例詳解
本文實例講述了Java泛型定義與用法。分享給大家供大家參考,具體如下:
1. 泛型的由來
先看如下代碼:
import java.util.List; import java.util.ArrayList; public class TestGeneric { @SuppressWarnings({ "rawtypes", "unchecked" }) public static void main(String[] args) { List list = new ArrayList(); list.add(1); list.add("1"); list.add(new Object()); System.out.println(list); // 取值 Integer var1 = (Integer) list.get(0); String var2 = (String) list.get(1); Object var3 = list.get(2); System.out.println(var1 + " " + var2 + " " + var3); } }
運行結(jié)果:
[1, 1, java.lang.Object@1db9742]
1 1 java.lang.Object@1db9742
這段代碼很簡單,將整形、字符串、對象放進list集合中,然后逐一取出。可以看出,由于List接口在定義時并不知道元素的類型,因此默認為Object,即任意類型元素進入list集合后都會自動裝箱。而取值的過程更為復(fù)雜,所有取得的值都是裝箱后的Object對象,必須得知道每一個元素的初始類型才能拆箱。一般使用集合的時候,集合的元素往往都是具有共同特征的,比如同屬于一類的----那么,如果一開始限定了list集合元素的類型,那么就可避免上述不規(guī)范操作。代碼如下,
import java.util.List; import java.util.ArrayList; public class TestGeneric { @SuppressWarnings("unused") public static void main(String[] args) { List<String> list = new ArrayList<String>(); // list.add(1);//報錯 // list.add(new Object());//報錯 list.add("1"); // 取值 String var1 = list.get(0);// 無需轉(zhuǎn)換 } }
如此一來,便有了泛型集合的說法。實際上,查閱List接口的Api會發(fā)現(xiàn),List接口正是泛型接口,它可以接受一個類型參數(shù)E,若不傳遞參數(shù),則默認是Object類型。
2. 泛型類型的繼承關(guān)系
有如下功能的代碼,實現(xiàn)打印任意集合的元素:
import java.util.List; import java.util.ArrayList; import java.util.Collection; public class TestGeneric{ //打印任意集合元素 public void print(Collection<Object> c){ System.out.println(c); } public static void main(String[] args){ List<String> list=new ArrayList<String>(); new TestGeneric().print(list); } }
輸出:
TestGeneric.java:11: 無法將 TestGeneric 中的 print(java.util.Collection<java.lang.Object>) 應(yīng)用于 (java.util.List<java.lang.String>)
new TestGeneric().print(list);
^
1 錯誤
很明顯,意思就是傳遞的參數(shù)類型不匹配。難道String不是繼承自O(shè)bject的嗎?沒錯,String是繼承自O(shè)bject的,但是List<String>
與List<Object>
是截然不同的兩個類型,兩者之間沒有任何繼承關(guān)系。那如果真的要實現(xiàn)上面的功能,該如何呢?
2.1 類型通配符
import java.util.List; import java.util.ArrayList; import java.util.Collection; public class TestGeneric { // 打印任意集合元素 public void print(Collection<?> c) { System.out.println(c); } public static void main(String[] args) { List<String> list = new ArrayList<String>(); new TestGeneric().print(list); } }
程序正常執(zhí)行,這里的?表示一個未知類型,這個未知類型與Object不同,List<?>代表了所有的List<類型>的父類。
2.2 泛型方法
不只有通配符可以解決泛型繼承的問題,若將上面的方法定義為泛型方法也具有同樣的效果:
import java.util.List; import java.util.ArrayList; import java.util.Collection; public class TestGeneric { // 打印任意集合元素 public <T> void print(Collection<T> c) { System.out.println(c); } public static void main(String[] args) { List<String> list = new ArrayList<String>(); new TestGeneric().print(list); } }
泛型方法的定義形式如下,
修飾符 <T,E> 返回值 方法名(形參)
其中<T,E>在修飾符的后面做為類型定義,為方法指明形參中需要用到的T,E類型是來自哪里。既然泛型方法和類型通配符都可以實現(xiàn)泛型中的繼承,那么有什么區(qū)別?
2.3 泛型方法和通配符的區(qū)別
看如下代碼:
import java.util.List; import java.util.ArrayList; import java.util.Collection; public class TestGeneric { // 打印任意集合元素 public <E, T extends E> void print(Collection<T> c1, Collection<E> c2) { System.out.println(c1); System.out.println(c2); } public static void main(String[] args) { List<Father> list1 = new ArrayList<Father>(); List<Father> list2 = new ArrayList<Father>(); new TestGeneric().print(list1, list2);// 傳2個father類型 List<Child> list3 = new ArrayList<Child>(); List<Father> list4 = new ArrayList<Father>(); new TestGeneric().print(list3, list4);// T為child,E為father List<Father> list5 = new ArrayList<Father>(); List<Child> list6 = new ArrayList<Child>(); new TestGeneric().print(list5, list6);// T為father,E為child,報錯 } } class Father { } class Child extends Father { } class Other { }
上述泛型方法在定義T,E時已經(jīng)指明了關(guān)系:T是E的子類,所以在傳遞參數(shù)的時候,T要么是E的子類,要么就是E本身,所以在傳遞關(guān)系不小心變?yōu)镋 exends T時,在第三次調(diào)用方法時報錯了。而如果把上述代碼換成?通配符的話,則不具有如此強的限定關(guān)系。
總之,泛型方法和?通配符都可以實現(xiàn)未知類型的繼承,但是泛型方法主要強調(diào)多個未知類型之間的依賴關(guān)系。如果只是單純用作成為一個通用類型的父類這一功能的話,兩者都可以實現(xiàn),反而?通配符較為簡潔明了。
2.4 泛型參數(shù)上、下限的注意
看如下代碼:
import java.util.List; import java.util.ArrayList; import java.util.Collection; public class TestGeneric { // 復(fù)制集合并返回原始集合的最后一個元素 public <T> T copy(Collection<T> des, Collection<? extends T> src) { T lastElement = null; for (T t : src) { lastElement = t; des.add(t); } return lastElement; } public static void main(String[] args) { List<Number> des = new ArrayList<Number>(); List<Integer> src = new ArrayList<Integer>(); src.add(new Integer(1)); Integer lastElement = new TestGeneric().copy(des, src);// System.out.println(lastElement.getClass()); } }
輸出:
TestGeneric.java:18: 不兼容的類型
找到: java.lang.Number
需要: java.lang.Integer
Integer lastElement= new TestGeneric().copy(des,src);//
^
1 錯誤
當(dāng)調(diào)用完copy方法后,系統(tǒng)比對發(fā)現(xiàn)T類型為Number,?類型為Integer。所以函數(shù)返回的T類型是Number了,所以根本不兼容Integer。要修改上面的代碼,有倆個辦法,
方法1:
改為
Number lastElement=new TestGeneric().copy(des,src);
分析代碼可以得出,?為T的子類,在方法中T=lastElement
這句表現(xiàn)為多態(tài),雖然返回的是T類型,但是多態(tài)的表現(xiàn)為?類型,即Interger類型,調(diào)用lastElement.getClass()
也可發(fā)現(xiàn)返回的是java.lang.Integer類型,說明此處編譯類型為T類型,實際運行類型為?類型。這就好比如下多態(tài)轉(zhuǎn)換,
Father f=new Child(); Child c=f;//此處一定報錯,類型不兼容
雖然f的多態(tài)表現(xiàn)為子類Child,但是上面一句連語法檢測都過不了。這也就是為什么上面Integer不能兼容Number的原因了。
方法2:
改為
public <T> T copy(Collection<? super T> des,Collection<T> src)
這樣一來,?類型變?yōu)榱烁割?,T類型變?yōu)榱俗宇悾谑窃诜椒ㄖ蟹祷氐腡類型對象,即lastElement就不具有多態(tài)性了。泛型中的上下限是很有學(xué)問的,每次看源碼時都會琢磨很久,但還是會在浩瀚的接口+泛型的設(shè)計中昏迷,這種設(shè)計真的完全是為了突出面向?qū)ο蟮奶匦?,以后慢慢琢磨吧?/p>
從這也再次可以看出?通配符在處理具有依賴關(guān)系的泛型方法中,顯得過于靈活而會導(dǎo)致一些潛在的隱患。
更多關(guān)于java算法相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java操作DOM節(jié)點技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對大家java程序設(shè)計有所幫助。
相關(guān)文章
Lombok為啥這么牛逼?SpringBoot和IDEA官方都要支持它
Lombok是一款Java代碼功能增強庫,在Github上已有9.8k+Star。這篇文章主要介紹了Lombok為啥這么牛逼?SpringBoot和IDEA官方都要支持它,需要的朋友可以參考下2020-12-12idea 實現(xiàn)git rebase操作應(yīng)用場景
本文結(jié)合idea工具進行rebase的各種場景的操作,借助工具更能直觀地觀察到分支之間地操作差異,方便我們理解rebase的各種操作以及場景的使用,對idea git rebase操作知識感興趣的朋友一起看看吧2024-01-01Java趣味練習(xí)題之輸出兩個日期之間的相隔天數(shù)
本篇文章介紹了我看到的一個趣味小題目,怎么求得兩個日期之間相隔的天數(shù),以及解決該題目的過程及思路,通讀本篇對大家的學(xué)習(xí)或工作具有一定的價值,需要的朋友可以參考下2021-10-10java根據(jù)模板導(dǎo)出PDF的詳細實現(xiàn)過程
前段時間因為相關(guān)業(yè)務(wù)需求需要后臺生成pdf文件,所以下面這篇文章主要給大家介紹了關(guān)于java根據(jù)模板導(dǎo)出PDF的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-02-02Java 創(chuàng)建動態(tài)類和查看方法列表信息的實例
這篇文章主要介紹了 Java 創(chuàng)建動態(tài)類和查看方法列表信息的實例的相關(guān)資料,需要的朋友可以參考下2017-06-06