我丢,去面试初级Java开发岗位,被问到泛型?
1、泛型的基礎概念
1.1 為什么需要泛型
List list = new ArrayList();//默認類型是Objectlist.add("A123");list.add("B234");list.add("C345");System.out.println(list);for(int i=0;i<list.size();i++){//若要將list中的元素賦給String變量,需要進行類型轉換,不然會報Incompatible types錯誤,顯示list.get(i)返回的是ObjectString str = (String) list.get(i);System.out.println(str);} ? list.add(123);//因為類型是Object,我們可以把Integer類型或者其他數據類型的元素也加入list之中System.out.println(list.get(3)); ?for(int i=0;i<list.size();i++){//String str = (String) list.get(i);//但是在這里會報錯java.lang.ClassCastException,我們不能直接將Integer類型的數據轉換成StringSystem.out.println(list.get(i).getClass());}如代碼中所示,當我們定義了一個List,list默認的類型是所有對象的基類Object,那么我們取出數據的時候需要經過一次類型轉換才能進行對象的實際類型的相關操作。因為List中的類型是Object,那么我們先添加了String類型的數據,然后再添加Integer或者其他類型的數據也是允許的,因為編譯時List中是Object類型的數據,然而運行的時候卻是它本身的類型,所以當我們將List中的數據當作String處理時會拋出java.lang.ClassCastException。
那么有沒有什么辦法可以使集合能夠記住集合內元素各類型,且能夠達到只要編譯時不出現問題,運行時就不會出現java.lang.ClassCastException異常呢?答案就是使用泛型。
1.2 什么是泛型
Java泛型設計原則是:只要在編譯時期沒有出現警告,那么運行時期就不會出現ClassCastException異常。
泛型,即“參數化類型”,把類型明確的工作推遲到創建對象或調用方法的時候才去明確的特殊類型,把<數據類型>當作是參數一樣傳遞。
相關術語:
-
ArrayList中的E稱為類型參數變量
-
ArrayList中的Integer稱為實際類型參數
-
整個稱為ArrayList泛型類型
-
整個ArrayList稱為參數化的類型ParameterizedType
泛型的作用:
-
代碼更加簡潔【不用強制轉換】
-
程序更加健壯【只要編譯時期沒有警告,那么運行時就不會拋出ClassCastException的異常】
-
可讀性和穩定性【在編寫集合的時候,就限定了類型】
泛型擦除
泛型是提供給javac編譯器使用的,它用于限定集合的輸入類型,讓編譯器在源代碼級別上,即擋住向集合中插入非法數據。但編譯器編譯完帶有泛型的java程序后生成的class文件中將不再帶有泛型信息,以此使程序的運行效率不受到影響,這個過程稱之為“擦除”。
泛型這個概念是JDK5提出的,JDK5以前的版本是沒有泛型的,需要建通JDK5以下的集合。當把帶有泛型特性的集合賦值給老版本的集合的時候,會把泛型給擦除了,它保留的是類型參數的上限,即Object。而當我們將沒有類型參數的集合賦給帶類型參數的集合,也不會報錯,僅僅是會提示“未經檢查的轉換(Unchecked assignment)”,甚至也可以將它賦給其他不同類型的帶有泛型特性的集合,只是依舊會拋出ClassCastException異常。
//類型被擦除了,保留的是類型的上限,String的上限就是ObjectList list = strlist; ?List<String> stringList2 = list;List<Integer> intList2 = list;//你也可以把它賦給Integer類型的集合,但是當你把這個集合當成Integer的集合操作的時候,依舊會拋出ClassCastException異常 ?for (Integer i:intList2){//java.lang.ClassCastExceptionSystem.out.println(i);}3、泛型的定義和使用
3.1 泛型類\泛型接口
泛型類、泛型接口就是把泛型定義在類或者接口上,在用戶使用該類的時候才把類型明確下來。我們常用的集合,List,Map<K,V>,Stack……就是泛型類。在類上定義的泛型,在泛型類的方法、變量中都可以使用。
由于類型參數變量T在java泛型中僅僅是一個占位符,在傳遞參數之后才能使用,即在完成實例創建之后才能使用,所以在泛型類中,不能定義包含泛型類型的靜態變量和靜態方法,會報錯cannot be referenced from a static context。泛型類中包含泛型類型的變量和方法必須在創建了實例明確了傳遞的類型參數后才可以使用。
class Myset<T>{private T value;//public static T sval;//cannot be referenced from a static contextpublic static int sval2;public Myset(){ ?} ?public Myset(T val){this.value = val;} ?public void setValue(T value) {this.value = value;} ?public T getValue() {return value;} ?/* public static T getSval(){//cannot be referenced from a static contextreturn sval;}*/ }Myset<String> myset = new Myset<>();myset.setValue("12345");System.out.println(myset.getValue());//12345 ?myset = new Myset<>("23");System.out.println(myset.getClass());//class liwx.learning.Myset3.2 泛型方法
public static <T> void PrintArray(T [] arr){System.out.print("[");for(T t:arr){System.out.print(t+",");}System.out.println("]");} Integer[] a = {1,2,3,4,5,6,7}; PrintArray(a);//[1,2,3,4,5,6,7,]3.3 泛型類的繼承
泛型類的子類有兩種繼承方式
-
子類不明確泛型類的參數變量,子類也是泛型類
-
子類明確泛型類的參數變量,子類不是泛型類
3.4 類型通配符?及其上下限
通配符<?>和類型參數變量的區別是什么?通配符<?>是實參而不是類型形參,而且List<?>在邏輯上是List,List等所有List<具體類型實參>的父類,它的使用比類型形參T更加靈活,但傳入的通配符通常進行的是許多于具體類型無關的操作,如果涉及到具體類型相關的操作,以及返回值,還是需要使用泛型方法T。
當我們使用?號通配符的時候,只能調用與對象無關的方法,不能調用對象與類型有關的方法。因為直到外界使用才知道具體的類型是什么。
//雖然Object是所有類的基類,但是List<Object>在邏輯上與List<Integer>等并沒有繼承關系,這個方法只能傳入List<Object>類型的數據 public static void showOList(List<Object> list){System.out.println(list.size());}//同理,這個方法只能傳入List<Number>類型的數據,并不能傳入List<Integer>public static void showList(List<Number> list){System.out.println(list.size());}//加入Java開發交流君樣:756584822一起吹水聊天//使用通配符,List<?>在邏輯上是所有List<Number>,List<Integer>,List<String>……的父類,可以傳遞所有List類型的數據,但是不能在List<?>類型的數據進行于具體類型相關的操作,如addpublic static void showList2(List<?> list){System.out.println("<?>");System.out.println(list.size());}//設置通配符上限,可以傳入List<Number及Number的子類>public static void showNumList(List<? extends Number> list){System.out.println(list.size());}//設置通配符上限,List<? super Number>只可以傳入List<Number及其父類>public static boolean Compare(List<? super Number> list1,List<? super Integer> list2){return list1.size()>list2.size();} List<Integer> Intgetlist = new ArrayList<>();List<Number> Numberlist = new ArrayList<>();//雖然Number是Integet的父類,但是傳入List,它們邏輯上沒有了繼承關系System.out.println(Intgetlist.getClass()==Numberlist.getClass());//true ?//加入Java開發交流君樣:756584822一起吹水聊天//showList(java.util.List<java.lang.Number>)//List<Integer>和List<Number>邏輯上無繼承關系,所以無法調用//showList(Intgetlist);//showList(java.util.List<java.lang.Number>)in FXtest cannot be applied to(java.util.List<java.lang.Integer>)showList(Numberlist); ?//public static void showList2(List<?> list)//通配符List<?>在邏輯上是所有List<具體參數類型>的父類,方法可以傳入其子類類型的數據showList2(Intgetlist);showList2(Numberlist); ?// public static void showNumList(List<? extends Number> list)//當設定了通配符上限,只能傳入List<Number及其子類>的數據List<String> Stringlist = new ArrayList<>();showNumList(Intgetlist);showNumList(Numberlist);//showNumList(Stringlist);//showNumList(java.util.List<? extends java.lang.Number>)in FXtest cannot be applied to(java.util.List<java.lang.String>) ? ?//public static boolean Compare(List<? super Number> list1,List<? super Integer> list2)//當設定了通配符下限,List<? super Number>只能傳入List<Number及其父類>的數據,不能傳入子類Integer的List,// 而List<? super Integer>則可以傳入其父類Number的List//Compare(Intgetlist,Numberlist);Compare(Numberlist,Intgetlist);通配符的使用在邏輯上還原了泛型類傳入數據類型的參數父類子類的繼承關系,同時也可以按照需求設定通配符的上限了下限。
-
List<?>在邏輯上是所有List<具體參數類型>的父類,可對所有List數據進行操作
-
List<? extends Type>設定了通配符的上限,可對所有Type及Type的子類進行操作
-
List<? super Type>設定了通配符的下限,可對所有Type及Type的父類進行操作
最后,祝大家早日學有所成,拿到滿意offer
總結
以上是生活随笔為你收集整理的我丢,去面试初级Java开发岗位,被问到泛型?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 因幼崽被狗杀害 印度猴子为报复几乎摔死全
- 下一篇: 阿里最新面试必备项之Java的Strin