Java開(kāi)發(fā)崗位面試被問(wèn)到泛型怎么辦
1、泛型的基礎(chǔ)概念
1.1 為什么需要泛型
List list = new ArrayList();//默認(rèn)類(lèi)型是Object
list.add("A123");
list.add("B234");
list.add("C345");
System.out.println(list);
for(int i=0;i<list.size();i++){
//若要將list中的元素賦給String變量,需要進(jìn)行類(lèi)型轉(zhuǎn)換,不然會(huì)報(bào)Incompatible types錯(cuò)誤,顯示list.get(i)返回的是Object
String str = (String) list.get(i);
System.out.println(str);
}
list.add(123);//因?yàn)轭?lèi)型是Object,我們可以把Integer類(lèi)型或者其他數(shù)據(jù)類(lèi)型的元素也加入list之中
System.out.println(list.get(3));
for(int i=0;i<list.size();i++){
//String str = (String) list.get(i);//但是在這里會(huì)報(bào)錯(cuò)java.lang.ClassCastException,我們不能直接將Integer類(lèi)型的數(shù)據(jù)轉(zhuǎn)換成String
System.out.println(list.get(i).getClass());
}
如代碼中所示,當(dāng)我們定義了一個(gè)List,list默認(rèn)的類(lèi)型是所有對(duì)象的基類(lèi)Object,那么我們?nèi)〕鰯?shù)據(jù)的時(shí)候需要經(jīng)過(guò)一次類(lèi)型轉(zhuǎn)換才能進(jìn)行對(duì)象的實(shí)際類(lèi)型的相關(guān)操作。因?yàn)長(zhǎng)ist中的類(lèi)型是Object,那么我們先添加了String類(lèi)型的數(shù)據(jù),然后再添加Integer或者其他類(lèi)型的數(shù)據(jù)也是允許的,因?yàn)榫幾g時(shí)List中是Object類(lèi)型的數(shù)據(jù),然而運(yùn)行的時(shí)候卻是它本身的類(lèi)型,所以當(dāng)我們將List中的數(shù)據(jù)當(dāng)作String處理時(shí)會(huì)拋出java.lang.ClassCastException。
那么有沒(méi)有什么辦法可以使集合能夠記住集合內(nèi)元素各類(lèi)型,且能夠達(dá)到只要編譯時(shí)不出現(xiàn)問(wèn)題,運(yùn)行時(shí)就不會(huì)出現(xiàn)java.lang.ClassCastException異常呢?答案就是使用泛型。
1.2 什么是泛型
Java泛型設(shè)計(jì)原則是:只要在編譯時(shí)期沒(méi)有出現(xiàn)警告,那么運(yùn)行時(shí)期就不會(huì)出現(xiàn)ClassCastException異常。
泛型,即“參數(shù)化類(lèi)型”,把類(lèi)型明確的工作推遲到創(chuàng)建對(duì)象或調(diào)用方法的時(shí)候才去明確的特殊類(lèi)型,把<數(shù)據(jù)類(lèi)型>當(dāng)作是參數(shù)一樣傳遞。
相關(guān)術(shù)語(yǔ):
- ArrayList中的E稱(chēng)為類(lèi)型參數(shù)變量
- ArrayList中的Integer稱(chēng)為實(shí)際類(lèi)型參數(shù)
- 整個(gè)稱(chēng)為ArrayList泛型類(lèi)型
- 整個(gè)ArrayList稱(chēng)為參數(shù)化的類(lèi)型
ParameterizedType
泛型的作用:
代碼更加簡(jiǎn)潔【不用強(qiáng)制轉(zhuǎn)換】
程序更加健壯【只要編譯時(shí)期沒(méi)有警告,那么運(yùn)行時(shí)就不會(huì)拋出ClassCastException的異常】
可讀性和穩(wěn)定性【在編寫(xiě)集合的時(shí)候,就限定了類(lèi)型】
List<String> strlist = new ArrayList<String>();
List<Integer> intlist = new ArrayList<Integer>();
strlist.add("A");
strlist.add("B");
//strlist.add(123);//編譯時(shí)報(bào)錯(cuò)
for(String str:strlist){
System.out.println(str);
//A
//B
}
//加入Java開(kāi)發(fā)交流君樣:756584822一起吹水聊天
System.out.println(strlist.getClass());//class java.util.ArrayList
System.out.println(intlist.getClass());//class java.util.ArrayList
System.out.println(strlist.getClass()==intlist.getClass());//true
泛型擦除
泛型是提供給javac編譯器使用的,它用于限定集合的輸入類(lèi)型,讓編譯器在源代碼級(jí)別上,即擋住向集合中插入非法數(shù)據(jù)。但編譯器編譯完帶有泛型的java程序后生成的class文件中將不再帶有泛型信息,以此使程序的運(yùn)行效率不受到影響,這個(gè)過(guò)程稱(chēng)之為“擦除”。
泛型這個(gè)概念是JDK5提出的,JDK5以前的版本是沒(méi)有泛型的,需要建通JDK5以下的集合。當(dāng)把帶有泛型特性的集合賦值給老版本的集合的時(shí)候,會(huì)把泛型給擦除了,它保留的是類(lèi)型參數(shù)的上限,即Object。而當(dāng)我們將沒(méi)有類(lèi)型參數(shù)的集合賦給帶類(lèi)型參數(shù)的集合,也不會(huì)報(bào)錯(cuò),僅僅是會(huì)提示“未經(jīng)檢查的轉(zhuǎn)換(Unchecked assignment)”,甚至也可以將它賦給其他不同類(lèi)型的帶有泛型特性的集合,只是依舊會(huì)拋出ClassCastException異常。
//類(lèi)型被擦除了,保留的是類(lèi)型的上限,String的上限就是Object
List list = strlist;
List<String> stringList2 = list;
List<Integer> intList2 = list;//你也可以把它賦給Integer類(lèi)型的集合,但是當(dāng)你把這個(gè)集合當(dāng)成Integer的集合操作的時(shí)候,依舊會(huì)拋出ClassCastException異常
for (Integer i:intList2){//java.lang.ClassCastException
System.out.println(i);
}
2、泛型的定義和使用
2.1 泛型類(lèi)\泛型接口
泛型類(lèi)、泛型接口就是把泛型定義在類(lèi)或者接口上,在用戶使用該類(lèi)的時(shí)候才把類(lèi)型明確下來(lái)。我們常用的集合,List,Map<K,V>,Stack……就是泛型類(lèi)。在類(lèi)上定義的泛型,在泛型類(lèi)的方法、變量中都可以使用。
由于類(lèi)型參數(shù)變量T在java泛型中僅僅是一個(gè)占位符,在傳遞參數(shù)之后才能使用,即在完成實(shí)例創(chuàng)建之后才能使用,所以在泛型類(lèi)中,不能定義包含泛型類(lèi)型的靜態(tài)變量和靜態(tài)方法,會(huì)報(bào)錯(cuò)cannot be referenced from a static context。泛型類(lèi)中包含泛型類(lèi)型的變量和方法必須在創(chuàng)建了實(shí)例明確了傳遞的類(lèi)型參數(shù)后才可以使用。
class Myset<T>{
private T value;
//public static T sval;//cannot be referenced from a static context
public static int sval2;
//加入Java開(kāi)發(fā)交流君樣:756584822一起吹水聊天
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 context
return sval;
}*/
}
Myset<String> myset = new Myset<>();
myset.setValue("12345");
System.out.println(myset.getValue());//12345
myset = new Myset<>("23");
//加入Java開(kāi)發(fā)交流君樣:756584822一起吹水聊天
System.out.println(myset.getClass());//class liwx.learning.Myset
2.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,]
2.3 泛型類(lèi)的繼承
泛型類(lèi)的子類(lèi)有兩種繼承方式
- 子類(lèi)不明確泛型類(lèi)的參數(shù)變量,子類(lèi)也是泛型類(lèi)
- 子類(lèi)明確泛型類(lèi)的參數(shù)變量,子類(lèi)不是泛型類(lèi)
//子類(lèi)不明確泛型類(lèi)的參數(shù)變量,子類(lèi)也是泛型類(lèi)
class MyChiSet1<T> extends Myset<T>{
public MyChiSet1(){
}
public MyChiSet1(T val){
super(val);
}
//加入Java開(kāi)發(fā)交流君樣:756584822一起吹水聊天
}
//子類(lèi)明確泛型類(lèi)的參數(shù)變量,子類(lèi)不是泛型類(lèi)
class MyChiSet2 extends Myset<String>{
public MyChiSet2(){
}
public MyChiSet2(String val){
super(val);
}
}
2.4 類(lèi)型通配符?及其上下限
通配符<?>和類(lèi)型參數(shù)變量的區(qū)別是什么?通配符<?>是實(shí)參而不是類(lèi)型形參,而且List<?>在邏輯上是List,List等所有List<具體類(lèi)型實(shí)參>的父類(lèi),它的使用比類(lèi)型形參T更加靈活,但傳入的通配符通常進(jìn)行的是許多于具體類(lèi)型無(wú)關(guān)的操作,如果涉及到具體類(lèi)型相關(guān)的操作,以及返回值,還是需要使用泛型方法T。
當(dāng)我們使用?號(hào)通配符的時(shí)候,只能調(diào)用與對(duì)象無(wú)關(guān)的方法,不能調(diào)用對(duì)象與類(lèi)型有關(guān)的方法。因?yàn)橹钡酵饨缡褂貌胖谰唧w的類(lèi)型是什么。
//雖然Object是所有類(lèi)的基類(lèi),但是List<Object>在邏輯上與List<Integer>等并沒(méi)有繼承關(guān)系,這個(gè)方法只能傳入List<Object>類(lèi)型的數(shù)據(jù)
public static void showOList(List<Object> list){
System.out.println(list.size());
}
//同理,這個(gè)方法只能傳入List<Number>類(lèi)型的數(shù)據(jù),并不能傳入List<Integer>
public static void showList(List<Number> list){
System.out.println(list.size());
}//加入Java開(kāi)發(fā)交流君樣:756584822一起吹水聊天
//使用通配符,List<?>在邏輯上是所有List<Number>,List<Integer>,List<String>……的父類(lèi),可以傳遞所有List類(lèi)型的數(shù)據(jù),但是不能在List<?>類(lèi)型的數(shù)據(jù)進(jìn)行于具體類(lèi)型相關(guān)的操作,如add
public static void showList2(List<?> list){
System.out.println("<?>");
System.out.println(list.size());
}
//設(shè)置通配符上限,可以傳入List<Number及Number的子類(lèi)>
public static void showNumList(List<? extends Number> list){
System.out.println(list.size());
}
//設(shè)置通配符上限,List<? super Number>只可以傳入List<Number及其父類(lèi)>
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的父類(lèi),但是傳入List,它們邏輯上沒(méi)有了繼承關(guān)系
System.out.println(Intgetlist.getClass()==Numberlist.getClass());//true
//加入Java開(kāi)發(fā)交流君樣:756584822一起吹水聊天
//showList(java.util.List<java.lang.Number>)
//List<Integer>和List<Number>邏輯上無(wú)繼承關(guān)系,所以無(wú)法調(diào)用
//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<具體參數(shù)類(lèi)型>的父類(lèi),方法可以傳入其子類(lèi)類(lèi)型的數(shù)據(jù)
showList2(Intgetlist);
showList2(Numberlist);
// public static void showNumList(List<? extends Number> list)
//當(dāng)設(shè)定了通配符上限,只能傳入List<Number及其子類(lèi)>的數(shù)據(jù)
List<String> Stringlist = new ArrayList<>();
showNumList(Intgetlist);
showNumList(Numberlist);//加入Java開(kāi)發(fā)交流君樣:756584822一起吹水聊天
//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)
//當(dāng)設(shè)定了通配符下限,List<? super Number>只能傳入List<Number及其父類(lèi)>的數(shù)據(jù),不能傳入子類(lèi)Integer的List,
// 而List<? super Integer>則可以傳入其父類(lèi)Number的List
//Compare(Intgetlist,Numberlist);
Compare(Numberlist,Intgetlist);
通配符的使用在邏輯上還原了泛型類(lèi)傳入數(shù)據(jù)類(lèi)型的參數(shù)父類(lèi)子類(lèi)的繼承關(guān)系,同時(shí)也可以按照需求設(shè)定通配符的上限了下限。
- List<?>在邏輯上是所有List<具體參數(shù)類(lèi)型>的父類(lèi),可對(duì)所有List數(shù)據(jù)進(jìn)行操作
- List<? extends Type>設(shè)定了通配符的上限,可對(duì)所有Type及Type的子類(lèi)進(jìn)行操作
- List<? super Type>設(shè)定了通配符的下限,可對(duì)所有Type及Type的父類(lèi)進(jìn)行操作
總結(jié)
本篇文章就到這里了,希望能給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
java定時(shí)任務(wù)框架elasticjob詳解
這篇文章主要介紹了java定時(shí)任務(wù)框架elasticjob詳解,Elastic-Job是ddframe中dd-job的作業(yè)模塊中分離出來(lái)的分布式彈性作業(yè)框架。該項(xiàng)目基于成熟的開(kāi)源產(chǎn)品Q(chēng)uartz和Zookeeper及其客戶端Curator進(jìn)行二次開(kāi)發(fā)。,需要的朋友可以參考下2019-06-06
EL表達(dá)式簡(jiǎn)介_(kāi)動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
EL全名為Expression Language,這篇文章主要給大家介紹EL表達(dá)式的主要作用及內(nèi)容簡(jiǎn)介,感興趣的朋友一起看看2017-07-07
解決idea中debug工具欄消失后如何顯示的問(wèn)題
這篇文章主要介紹了解決idea中debug工具欄消失后如何顯示的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02
Spring?Boot各類(lèi)變量的使用小結(jié)
這篇文章主要介紹了Spring?Boot各類(lèi)變量的使用小結(jié),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-01-01
初識(shí)Java基礎(chǔ)之?dāng)?shù)據(jù)類(lèi)型與運(yùn)算符
Java是一種強(qiáng)類(lèi)型語(yǔ)言,每個(gè)變量都必須聲明其數(shù)據(jù)類(lèi)型,下面這篇文章主要給大家介紹了關(guān)于Java基礎(chǔ)之?dāng)?shù)據(jù)類(lèi)型與運(yùn)算符的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-10-10
ssm實(shí)現(xiàn)分頁(yè)查詢(xún)的實(shí)例
下面小編就為大家?guī)?lái)一篇ssm實(shí)現(xiàn)分頁(yè)查詢(xún)的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
Spring Aop組成部分及實(shí)現(xiàn)步驟
面向切面編程,是對(duì)面向?qū)ο缶幊痰囊环N補(bǔ)充,是一種編程思想,是對(duì)某一類(lèi)的事情的集中處理,這篇文章主要介紹了Spring Aop組成部分及實(shí)現(xiàn)步驟,需要的朋友可以參考下2023-08-08
IntelliJ IDEA 2020.1.2激活工具下載及破解方法免費(fèi)可用至2089年(強(qiáng)烈推薦)
這篇文章主要介紹了IntelliJ IDEA 2020.1.2激活工具下載及破解方法免費(fèi)可用至2089年(強(qiáng)烈推薦),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
Java老手該當(dāng)心的13個(gè)錯(cuò)誤
這篇文章主要介紹了Java老手該當(dāng)心的13個(gè)錯(cuò)誤,需要的朋友可以參考下2015-04-04

