認真看這篇文章哈踱,保證你們對泛型又有新的理解,如果沒有的話嚷兔,請順著網(wǎng)線來打我呀还惠。
概述
引用下百度百科的回答
泛型是程序設計語言的一種特性断箫。允許程序員在強類型程序設計語言中編寫代碼時定義一些可變部分馍管,那些部分在使用前必須作出指明钾麸。各種程序設計語言和其編譯器、運行環(huán)境對泛型的支持均不一樣鲫趁。將類型參數(shù)化以達到代碼復用提高軟件開發(fā)工作效率的一種數(shù)據(jù)類型。泛型類是引用類型利虫,是堆對象挨厚,主要是引入了類型參數(shù)這個概念。
我的理解是:泛型就是把類型明確的工作推遲到創(chuàng)建對象或調用方法的時候才去明確的特殊的類型糠惫。
參數(shù)化類型疫剃,把類型當作是參數(shù)一樣傳遞,Object<數(shù)據(jù)類型>這里面只能是引用類型不能是基本類型硼讽。
比如:
Object<Integer> //true
Object<int> //false
為什么泛型里面數(shù)據(jù)類型不能是基本類型呢巢价?
因為虛擬機在編譯時會把帶泛型的轉換成Object類型,而基本類型不屬于Object類型,所以泛型里面數(shù)據(jù)類型不能是基本類型壤躲。
為什么要使用泛型呢城菊?
Java語言引入泛型的好處是安全簡單。泛型的好處是在編譯的時候檢查類型安全碉克,并且所有的強制轉換都是自動和隱式的凌唬,提高代碼的重用率。
有一點很重要就是消除了強制類型轉換漏麦,減少了出錯機會客税,舉個例子:
public class Test {
public static void main(String[] args){
List list = new ArrayList();
list.add("1");
list.add(1);
int i = (int)list.get(0); // java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
int j = (int)list.get(1);
}
}
上面代碼在編譯期沒有問題,因為list的add方法是Object類型撕贞,所以在編譯期沒有什么問題更耻,但是運行期的時候我們調用 list的時候并不知道list里面具體存了什么類型的參數(shù),所以取的時候有可能就會報類型轉換的錯誤 捏膨。
如果用了泛型上面的錯誤就不會發(fā)生了秧均。
public class Test {
public static void main(String[] args){
List<String> list = new ArrayList();
list.add("1");
list.add(1);//在編譯期就會出錯,因為用了泛型脊奋,虛擬機就會在編譯期的時候檢查泛型類型安全熬北。
}
}
泛型只存在于編譯期。
舉個例子诚隙。
public class Test {
public static void main(String[] args) throws Exception{
List<String> list = new ArrayList();
list.add("hello");
//list.add(23) //編譯期會報錯
Class c = Class.forName("java.util.ArrayList");
Method m = c.getMethod("add",Object.class);
m.invoke(list,23);
System.out.println(list); // [hello, 23]
}
}
通過上面可知讶隐,泛型只在編譯期有效,為什么運行期失效了呢久又,這是因為泛型的擦除概念巫延,通俗點來說就是泛型的信息不會進行運行階段。
泛型的使用
泛型有三種實用方式
泛型類:public class Test<T>}{}
T表示未知類型
泛型接口: public interface Test<T>{}
和定義類一樣
泛型方法:public <T> void Test(T name){}
泛型類的使用
泛型類在java中有著很重要的地位地消,其中我們用的最多的就是ArrayList,HashMap,HashSet.
既然是帶你看懂Java泛型炉峰,肯定不能源碼里面的那些容器類呀,那些容器類都已經(jīng)很完善了如果要帶你們看的話脉执,肯定會越看越暈的疼阔,所以我們自己定義一個泛型類出來。
//這個T可以換成隨便一個字母 半夷,只不過我寫泛型都用的T婆廊,你可以換成A,B,C...
public class Test<T> {
T name;
public Test(T name){
this.name = name;
}
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
//如果不傳泛型類型的話巫橄,那么默認的就是Object型什么都可以傳
Test test = new Test("hello");
//傳入的數(shù)據(jù)類型不為基本類型淘邻,否則編譯期會報錯,開頭我解釋過為什么會報錯了
Test<Integer> test1 = new Test<>(418);
泛型接口的使用
泛型接口的定義和泛型類的定義差不多湘换,我們常見的泛型接口就是宾舅,List,Map,Set.
首先老規(guī)矩我們自己定義一個泛型接口统阿。
public interface Test<T>{
T getName(T name);
}
//如果實現(xiàn)接口的時候不傳入數(shù)據(jù)類型的話,需要將泛型聲明也要寫到類中要不然會報錯
class Test1<T> implements Test<T>{
@Override
public T getName(T name) {
return null;
}
}
//實現(xiàn)接口的時候傳入數(shù)據(jù)類型的話筹我,就不用把泛型聲明也寫到類中了
class Test2 implements Test<String>{
@Override
public String getName(String name) {
return name;
}
}
泛型方法的使用
泛型方法的使用
public <T> void getName(T name){}
public <T,K> void getNameAndValue(T name, K value){}
public <T,K,V> void getNameAndValueAndV(T name, K value, V v){}//總的來說就是參數(shù)需要多少泛型扶平,返回值前面就得定義幾個泛型要不然編譯期會出錯
泛型通配符
為什么要用通配符呢?
java里面類和類之間是有繼承關系 的崎溃,比如Cat extends Animals,那么Cat就是Animal的子類蜻直,但是集合是沒有繼承這個概念的,比如List<Cat> catList
和List<Animals> animalList
你不能說 animalList是catList的父類袁串,所以很難看出來這兩個類之間的聯(lián)系概而,但是我們現(xiàn)在只想讓list里面只加入Animals的子類怎么辦呢?
一種是Animals有多少個子類就定義多少個list囱修,這種方法雖然也可以實現(xiàn)但是Animals如果有一百個赎瑰,一千個,一萬個子類呢你這種方法是不是就太耗時了呢破镰。
第二種就是用通配符來實現(xiàn)餐曼。比如:
List<? extends Animals> animals
這個時候animals就只能添加Animals的子類了,一個list搞定鲜漩。
通配符的基本概念源譬?
-
無邊界的通配符:? 舉個例子,能接收所有未知類型的泛型
public class Test { public static void main(String []args){ List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); list.add(4); List<String> stringList = new ArrayList<>(); stringList.add("h"); stringList.add("e"); stringList.add("l"); stringList.add("l"); stringList.add("o"); getList(stringList); getList(list); } //無論傳入什么List都會被接收 public static List getList(List<?> list){ return list; }
用List<?>聲明的List 不能使用add方法孕似,因為你不知道的類型是什么踩娘,但是list.add(null)就可以,因為null是所有類型都有的喉祭。舉個例子
public static List getList(List<?> list){ // list.add(1);//會報參數(shù)不匹配的錯誤,編譯期報錯 // list.add("hello");//會報參數(shù)不匹配的錯誤,編譯期報錯 list.add(null);//添加成功 return list; }
用get方法也只能用Object來接收养渴,因為你不知道你的類型是什么。
public static List getList(List<?> list){ int i = list.get(0); //編譯期報錯 String j = list.get(1); //編譯期報錯 Object o = list.get(3); //運行正確 return list; }
-
上邊界通配符號:<? extends E> 可以接收E以及E的子類型的泛型泛烙,這里面的E不止是類哦理卑,也可以是接口,看個例子蔽氨。
//這個是繼承了類的用法 public class Test { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); getList(list); List<String> strings = new ArrayList<>(); strings.add("hello"); getList(strings);//編譯期報錯 } public static List getList(List<? extends Number> list) { return list; } }
public class Test { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); getList(list);// 編譯期報錯 List<Test2> test2s = new ArrayList<>(); getList(test2s); } //上邊界為接口的實現(xiàn)藐唠,只要是實現(xiàn)了此接口的類都可以被當做泛型傳進來 public static List getList(List<? extends Test1> list) { return list; } } interface Test1{ } class Test2 implements Test1{}
以上可知上邊界<? extends E>就是你傳入的類型必須得是E的子類,或者是實現(xiàn)接口的類鹉究。
-
下邊界通配符號:<? super E> 就是傳入的類型必須得是E以及E的父類中捆,舉個例子
public class Test { public static void main(String[] args) { List<Animals> animals = new ArrayList<>(); getList(animals); List<Cat> cats = new ArrayList<>(); getList(cats); List<Dog> dogs = new ArrayList<>(); getList(dogs);//編譯出錯,因為Dog不是Cat的父類 } public static List getList(List<? super Cat> list) { return list; } } class Animals{} class Cat extends Animals{} class Dog extends Animals{}
The End
我覺得在java里面我們用的最多的泛型就是List,Map這兩個了吧坊饶,其實我們自己編程的時候多用用泛型,可以減少一定的代碼量殴蓬,還能讓代碼看起來更好看 匿级,用雷軍的一句話說蟋滴,別人都說我寫的代碼像詩一樣,希望你們能在java的路上越走遠啊痘绎。
最后津函,喜歡這篇文章的話麻煩的你們的小手點個轉發(fā)點個贊再走吧,你們的每個轉發(fā)點贊都是我寫作的動力孤页。
關注千玨(jue)公眾號尔苦,后臺留言給我一起交流呀。