Java中的紙老虎之泛型
泛型,其實(shí)算是Java當(dāng)中比較難的語(yǔ)法了,很多人一開(kāi)始都對(duì)其一知半解,也很害怕閱讀帶泛型的源碼,雖然看起來(lái)語(yǔ)法很難,但當(dāng)你理解后會(huì)覺(jué)得很簡(jiǎn)單,其實(shí)只是一個(gè)紙老虎罷了。下面,我將會(huì)用非常簡(jiǎn)單易懂的方式帶你去理解它,相信你在認(rèn)真看完后會(huì)有非常大的收獲,從此不會(huì)再畏懼它!
一. 泛型的定義
這里大家可以不必去看網(wǎng)上的有些定義,因?yàn)橄鄬?duì)于比較學(xué)術(shù)化,只需記住泛型可以在程序設(shè)計(jì)中指定某種類型,讓程序的設(shè)計(jì)更加規(guī)范化即可
二. 為什么要用到泛型
了解到了泛型是什么后,那我們來(lái)討論討論為什么要用泛型這個(gè)語(yǔ)法,這個(gè)語(yǔ)法到底是干什么的?別急,下面,我先給大家舉一個(gè)例子:
class Stack { public Object[] objects; public int top; public Stack() { this.objects =new Object[10]; } public void push(Object obj) { objects[this.top++] = obj; } public Object get() { return objects[this.top-1]; } }
大家可以看看這是在干什么呢?這是我們自己寫(xiě)了一個(gè)棧,然后將棧里的數(shù)組類型設(shè)置成Object類型,這樣的話這個(gè)棧里任意類型的數(shù)據(jù)都可以存放了(Object類是任何類的父類,不管插入什么類型的數(shù)據(jù),都可以發(fā)生向上轉(zhuǎn)型)
下面,我們來(lái)測(cè)試一下
public class Test { public static void main(String[] args) { Stack stack=new Stack(); stack.push(1); stack.push(2); stack.push("123"); String str=(String)stack.get(); } }
可以看到,我們可以向自己寫(xiě)的棧里放入整形以及字符串等等任何類型的數(shù)據(jù),但注意一下取出數(shù)據(jù)的時(shí)候要進(jìn)行強(qiáng)制類型轉(zhuǎn)換
以上這樣寫(xiě),可以向棧里存放任何類型的數(shù)據(jù),比較通用,其優(yōu)點(diǎn)也可以變成缺點(diǎn),正因?yàn)樘ㄓ昧耍勾a的規(guī)范性降低,看起來(lái)比較凌亂,這時(shí)候,我們可以考慮使用泛型,這樣可以在類中或者Java集合中存放特定的數(shù)據(jù)(使用Java集合時(shí),一般都要用到泛型,而自定義的類型中可以使用泛型也可以不使用)
三. 泛型的寫(xiě)法
以自定義的類型為例,寫(xiě)法為在類名后面加上尖括號(hào),里面寫(xiě)上一個(gè)字母(注意,此處寫(xiě)任何字母都可以,只起到一個(gè)標(biāo)記這個(gè)類為泛型類的作用)
class Stack<T>
而在new對(duì)象時(shí),以棧里只能存放整形為例,前面的尖括號(hào)必須寫(xiě)基本數(shù)據(jù)類型對(duì)應(yīng)的包裝類,而后面的尖括號(hào)可以不用寫(xiě),示例如下:
Stack<Integer> stack = new Stack<>();
補(bǔ)一下Java中的基本數(shù)據(jù)類型與對(duì)應(yīng)的包裝類:
因此,我們前面寫(xiě)的自定義的??梢詫?xiě)成以下形式(以存放整形為例):
class Stack<T> { public T[] objects; public int top; public Stack() { this.objects = (T[])new Object[10]; } public void push(T obj) { objects[this.top++] = obj; } public T get() { return objects[this.top-1]; } } Stack<Integer> stack = new Stack<>(); stack.push(1); stack.push(2); int ret = stack.get(); System.out.println(ret);
特別注意此處:public Stack() { this.objects = (T[])new Object[10]; }
這里不能寫(xiě)成this.objects=new T[10];
原因:
1. 不能new泛型類型的數(shù)組
2. 也可理解為泛型是先檢查后編譯的,如果new泛型類型的數(shù)組的話,編譯器檢查時(shí)并不知道T是什么類型的,因此會(huì)報(bào)錯(cuò)。而編譯的時(shí)候才會(huì)進(jìn)行擦除機(jī)制,都會(huì)將其轉(zhuǎn)換為Object類型
3. 正因?yàn)橛羞@個(gè)擦除機(jī)制,這里才能進(jìn)行數(shù)組整體強(qiáng)制類型轉(zhuǎn)換(一般數(shù)組不能整體進(jìn)行強(qiáng)制類型轉(zhuǎn)換),因?yàn)榉盒椭皇窃诰幾g的時(shí)候起作用,而實(shí)際運(yùn)行時(shí)都會(huì)被擦除成Object類型,即實(shí)際運(yùn)行時(shí)是沒(méi)有泛型這個(gè)概念的,也即實(shí)際運(yùn)行時(shí)類型都是一樣的,所以T本質(zhì)上是object類型的,所以此代碼等價(jià)于不進(jìn)行強(qiáng)制類型轉(zhuǎn)換?。?!
4.而直接指定泛型的代碼(不是T) 比如:Stack<Integer>和Stack<Character>都是在運(yùn)行時(shí)直接把尖括號(hào)里的類型擦掉了,可以看到直接打印的結(jié)果(并沒(méi)有打印出類型):
此處注意多理解理解
四. 泛型的使用實(shí)例
1. 求最大值
以上就是泛型的一個(gè)重要知識(shí)點(diǎn)了,但光看是不夠的,還是得通過(guò)例子讓大家有一個(gè)更為深入的理解,比如,如何寫(xiě)一個(gè)泛型類來(lái)求數(shù)組的最大值呢?
基本的框架大概是這樣的:(沒(méi)看懂的小可愛(ài)好好看看上面講的內(nèi)容哦)
class Algorithm<T extends Comparable<T>> { public T findMax(T[] array) { T max = array[0]; for (int i = 1; i < array.length; i++) { if(max < array[i]) { max = array[i]; } } return max; } }
但是此代碼if(max < array[i])
會(huì)報(bào)錯(cuò),為什么呢?因?yàn)閷?lái)給T傳的值一定是一個(gè)引用類型,引用類型不能直接比較大于或者小于的,是要用Comparable或Comparator接口里的方法比較的,
因?yàn)榉盒驮诰幾g的時(shí)候會(huì)被擦除成Object類型,但Object類本身并沒(méi)有實(shí)現(xiàn)Comparable
或Comparator
接口,所以我們要控制其不要擦除到Object類,所以要給泛型指定一個(gè)邊界
具體寫(xiě)法如下:
class Algorithm<T extends Comparable<T>> { public T findMax(T[] array) { T max = array[0]; for (int i = 1; i < array.length; i++) { //max < array[i] if(max.compareTo(array[i]) < 0) { max = array[i]; } } return max; } }
class Algorithm<T extends Comparable<T>>
注意,extends叫做上界,此代碼代表的意思為T(mén)這個(gè)泛型類會(huì)擦除到實(shí)現(xiàn)了Comparable接口的地方,換句話說(shuō),這個(gè)T類型一定是實(shí)現(xiàn)了Comparable接口的
同理:這個(gè)代碼public class MyArrayList<E extends Number> { ... }
代表E為Number的子類或Number本身
下面讓我們來(lái)用一下:
Algorithm<Integer> algorithm1 = new Algorithm<>(); Integer[] integers = {1,2,13,4,5}; Integer ret = algorithm1.findMax(integers); System.out.println(ret);
運(yùn)行結(jié)果如下:
成功了!
2. 優(yōu)化
經(jīng)過(guò)上面的努力,我們已經(jīng)寫(xiě)出了一個(gè)泛型類來(lái)求一個(gè)數(shù)組的最大值了,但是,上面的例子是一個(gè)整形數(shù)組,那么我們能不能在數(shù)組里存放別的類型去比較呢?答案是可以的,但是我們還得去new一個(gè)對(duì)象,例如:Algorithm<String> algorithm2 = new Algorithm<>();
這樣很麻煩。但是我們可以將求最大值的方法設(shè)置成靜態(tài)的class Algorithm2 <T>
,因?yàn)槭庆o態(tài)的方法,不需要new對(duì)象,所以就沒(méi)有在new對(duì)象時(shí)指定泛型的過(guò)程了,所以沒(méi)必要給方法后加尖括號(hào),但是去掉之后,代碼又會(huì)被錯(cuò):
我們可以這樣修改:
class Algorithm2 { public static<T extends Comparable<T>> T findMax(T[] array) { T max = array[0]; for (int i = 1; i < array.length; i++) { if(max.compareTo(array[i]) < 0) { max = array[i]; } } return max; } }
此方法public static<T extends Comparable<T>> T findMax(T[] array){}
叫做泛型方法
下面繼續(xù)帶大家來(lái)用一下:
public static void main(String[] args) { Integer[] integers = {1,2,13,4,5}; //會(huì)根據(jù)形參的類型推導(dǎo)出整個(gè)泛型的類型參數(shù) Integer ret = Algorithm2.findMax(integers); System.out.println(ret); Integer ret2 = Algorithm2.<Integer>findMax(integers); System.out.println(ret2); }
注意,ret1寫(xiě)法和ret2寫(xiě)法是一樣的,都可以
打印結(jié)果如下:
五. 通配符
1. 基本寫(xiě)法
通配符也是泛型的一種,下面我們來(lái)寫(xiě)一個(gè)泛型方法來(lái)打印集合中的元素
class Test { public static<T> void print(ArrayList<T> list) { for (T t : list) { System.out.println(t); } }
這個(gè)寫(xiě)法很簡(jiǎn)單,上文都講過(guò)了,那么讓我們來(lái)試著用一下吧:
public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); Test.print(list); }
打印的結(jié)果如下:
除了以上這種寫(xiě)法,我們還可以將其改成通配符的寫(xiě)法,先給大家上代碼:
//?代表通配符 擦除機(jī)制 Object public static void print2(ArrayList<?> list) { for (Object t : list) { System.out.println(t); } } }
此處for (Object t : list)
必須這樣寫(xiě),因?yàn)橥ㄅ浞彩怯胁脸龣C(jī)制的,會(huì)在編譯器編程O(píng)bject類型。
2. 上界
語(yǔ)法:<? extends 上界>
示例:
public static void printAll(MyArrayList<? extends Number> list) { ... }
代表可以傳入類型實(shí)參是 Number 子類的任意類型的 MyArrayList
所以以下調(diào)用都是正確的:
printAll(new MyArrayList<Integer>()); printAll(new MyArrayList<Double>()); printAll(new MyArrayList<Number>());
以下調(diào)用都是錯(cuò)誤的:
printAll(new MyArrayList<String>()); printAll(new MyArrayList<Object>());
3. 下界
下界和上界的用法很類似
語(yǔ)法:<? super 下界>
示例:
public static void printAll(MyArrayList<? super Integer> list) { ... }
代表可以傳入類型實(shí)參是 Integer 父類的任意類型的 MyArrayList
所以以下調(diào)用是正確的:
printAll(new MyArrayList<Integer>()); printAll(new MyArrayList<Number>()); printAll(new MyArrayList<Object>());
以下調(diào)用是錯(cuò)誤的:
printAll(new MyArrayList<String>()); printAll(new MyArrayList<Double>());
六. 泛型的限制
學(xué)習(xí)完后,我們應(yīng)該注意泛型使用過(guò)程中以下一些限制:
泛型類型參數(shù)不支持基本數(shù)據(jù)類型
無(wú)法實(shí)例化泛型類型的對(duì)象
無(wú)法使用泛型類型聲明靜態(tài)的屬性
無(wú)法使用 instanceof 判斷帶類型參數(shù)的泛型類型(因?yàn)楸徊脸龣C(jī)制擦除了)
無(wú)法創(chuàng)建泛型類數(shù)組
無(wú)法 create、catch、throw 一個(gè)泛型類異常(異常不支持泛型)
泛型類型不是形參一部分,無(wú)法重載
好啦,本次泛型知識(shí)點(diǎn)的分享就先告一段落了,整理不易,但如果能幫到大家很開(kāi)心了。也希望大家多理解理解,不論是剛開(kāi)始學(xué)習(xí)還是復(fù)習(xí),都值得仔細(xì)揣摩哦!一起加油吧!
到此這篇關(guān)于Java中的紙老虎之泛型的文章就介紹到這了,更多相關(guān)Java 泛型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java讀寫(xiě)excel文件實(shí)現(xiàn)POI解析Excel的方法
在日常工作中,我們常常會(huì)進(jìn)行Excel文件讀寫(xiě)操作,這篇文章主要介紹了java讀寫(xiě)excel文件實(shí)現(xiàn)POI解析Excel的方法,實(shí)例分析了java讀寫(xiě)excel的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2018-10-10Java項(xiàng)目中大批量數(shù)據(jù)查詢導(dǎo)致OOM的解決
本文主要介紹了Java項(xiàng)目中大批量數(shù)據(jù)查詢導(dǎo)致OOM的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06Java中RabbitMQ延遲隊(duì)列實(shí)現(xiàn)詳解
這篇文章主要介紹了Java中RabbitMQ延遲隊(duì)列實(shí)現(xiàn)詳解,消息過(guò)期后,根據(jù)routing-key的不同,又會(huì)被死信交換機(jī)路由到不同的死信隊(duì)列中,消費(fèi)者只需要監(jiān)聽(tīng)對(duì)應(yīng)的死信隊(duì)列進(jìn)行消費(fèi)即可,需要的朋友可以參考下2023-09-09java實(shí)現(xiàn)AES 32位加密解密的方案
Oracle在其官方網(wǎng)站上提供了無(wú)政策限制權(quán)限文件(Unlimited Strength Jurisdiction Policy Files),我們只需要將其部署在JRE環(huán)境中,就可以解決限制問(wèn)題,下面給大家介紹下java實(shí)現(xiàn)AES 32位加密解密的方案,感興趣的朋友一起看看吧2021-11-11為什么在重寫(xiě) equals方法的同時(shí)必須重寫(xiě) hashcode方法
Object 類是所有類的父類,其 equals 方法比較的是兩個(gè)對(duì)象的引用指向的地址,hashcode 是一個(gè)本地方法,返回的是對(duì)象地址值。他們都是通過(guò)比較地址來(lái)比較對(duì)象是否相等的2016-07-07Java中用內(nèi)存映射處理大文件的實(shí)現(xiàn)代碼
下面小編就為大家?guī)?lái)一篇Java中用內(nèi)存映射處理大文件的實(shí)現(xiàn)代碼。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-06-06