您好,登錄后才能下訂單哦!
這篇文章主要介紹了Java泛型是什么,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
什么是泛型
為什么需要泛型
如何使用泛型
如何自定義泛型
類型通配符等知識
泛型不只是 Java 語言所特有的特性,泛型是程序設計語言的一種特性。
允許程序員在強類型的程序設計語言中編寫代碼時定義一些可變部分,那些部分在使用前必須做出聲明。
Java 中的集合類是支持泛型的,它在代碼中是這個樣子的
代碼中的<Integer>
就是泛型,我們把類型像參數一樣傳遞,尖括號中間就是數據類型,我們可以稱之為實際類型參數,這里實際類型參數的數據類型只能為引用數據類型。
那么為什么需要泛型呢?
我們在使用ArrayList
實現類的時候,如果沒有指定泛型,IDEA
會給出警告,代碼似乎也是可以順利運行的。請看如下實例:
import java.util.ArrayList; public class testDemo1 { public static void main(String[] args) { ArrayList arrayList = new ArrayList(); arrayList.add("Hello"); String str1 = (String) arrayList.get(0); System.out.println("str=" + str1); } }
運行結果:
str1=Hello
雖然運行時沒有發生任何異常,但這樣做有兩個缺點:
需要強制類型轉換: 由于ArrayList
內部就是一個Object[]
數組,在get()
元素的時候,返回的是Object
類型,所以在ArrayList
外獲取該對象,需要強制類型轉換。其它的Collection
、Map
如果不使用泛型,也存在這個問題;
可向集合中添加任意類型的對象,存在類型不安全風險。例如如下代碼中,我們向列表中既添加了Integer
類型,又添加了String
類型:
package com.caq.oop.demo08; import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { //實例化一個空列表 List arrayList = new ArrayList<>(); arrayList.add(123); arrayList.add("sad"); String str = (String) arrayList.get(0); } }
Exception in thread “main” java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at com.caq.oop.demo08.Test.main(Test.java:12)
由于我們的“疏忽”,列表第 1 個元素實際上是整型,但被我們強制轉換為字符串類型,這是行不通的,因此會拋出ClassCastException
異常。
使用泛型可以解決這些問題。泛型有如下優點:
可以減少類型轉換的次數,代碼更加簡潔;
程序更加健壯:只要編譯期沒有警告,運行期就不會拋出ClassCastException
異常;
提高了代碼的可讀性:編寫集合的時候,就限定了集合中能存放的類型。
在代碼中,這樣使用泛型:
List<String> list = new ArrayList<String>(); // Java 7 及以后的版本中,構造方法中可以省略泛型類型: List<String> list = new ArrayList<>(); 外幣巴伯
要注意的是,變量聲明的類型必須與傳遞給實際對象的類型保持一致,下面是錯誤的例子:
List<Object> list = new ArrayList<String>(); List<Number> numbers = new ArrayList(Integer);
在自定義泛型類之前,我們來看下java.util.ArrayList
是如何定義的:
類名后面的<E>
就是泛型的定義,E
不是 Java 中的一個具體的類型,它是 Java 泛型的通配符(注意是大寫的,實際上就是Element
的含義),可將其理解為一個占位符,將其定義在類上,使用時才確定類型。
此處的命名不受限制,但最好有一定含義,例如java.lang.HashMap
的泛型定義為HashMap<K,V>
,K
表示Key
,V
表示Value
。
下面我們來自定義一個泛型類,自定義泛型按照約定俗成可以叫<T>
,具有Type
的含義,實例如下:
實例演示
package com.caq.List; public class Generic01<T> { private T abc;//定義在類上的泛型,在類內部可以使用 public T getAbc() { return abc; } public void setAbc(T abc) { this.abc = abc; } public static void main(String[] args) { //實例化對象,指定元素類型為整型 Generic01<Integer> integerGeneric01 = new Generic01<>(); //調用方法 integerGeneric01.setAbc(100); System.out.println("integerGeneric01="+ integerGeneric01.getAbc()); //實例化對象,指定元素類型為長類型 Generic01<Long> longGeneric01 = new Generic01<>(); longGeneric01.setAbc(200L); System.out.println("longGeneric01="+ longGeneric01.getAbc()); // 實例化對象,指定元素類型為雙精度浮點型 Generic01<Double> doubleGeneric01 = new Generic01<>(); doubleGeneric01.setAbc(300.0); System.out.println("doubleGeneric01="+ doubleGeneric01.getAbc()); } }
運行結果:
integerGeneric01=100
longGeneric01=200
doubleGeneric01=300.0
我們在類的定義處也定義了泛型:Generic01<T>
;在類內部定義了一個T
類型的abc
變量,并且為其添加了setter
和getter
方法。
解釋:對于泛型類的使用也很簡單,在主方法中,創建對象的時候指定T
的類型分別為Integer
、Long
、Double
,類就可以自動轉換成對應的類型了。
上面我們知道了如何定義含有單個泛型的類,那么對于含有多個泛型的類,如何定義呢?
我們可以看一下HashMap
類是如何定義的。如下是 Java 源碼的截圖:
參照HashMap<K,V>
類的定義,下面我們來看看如何定義含有兩個泛型的類
package com.caq.List; public class Generic02<K, V> {//這次是定義兩個泛型在類上 //定義類型為K的key屬型 private K key; //定義類型為V的value屬型 private V value; //封裝里的知識,通過Getter和Setter方法來設置和獲取私有屬型的值 public K getKey() { return key; } public void setKey(K key) { this.key = key; } public V getValue() { return value; } public void setValue(V value) { this.value = value; } public static void main(String[] args) { //實例化對象,分別指定類型為整型,長整型 Generic02<Integer, Long> integerLongGeneric02 = new Generic02<>(); //實例化對象,分別指定類型為浮點型、字符串類型 Generic02<Float, String> floatStringGeneric02 = new Generic02<>(); integerLongGeneric02.setKey(100); integerLongGeneric02.setValue(200L); System.out.println("key=" + integerLongGeneric02.getKey()); System.out.println("value=" + integerLongGeneric02.getValue()); floatStringGeneric02.setKey(0.9f); floatStringGeneric02.setValue("巴啦啦能量"); System.out.println("key=" + floatStringGeneric02.getKey()); System.out.println("value=" + floatStringGeneric02.getValue()); } }
運行結果:
key=100value=200key=0.9value=巴啦啦能量
前面我們知道了如何定義泛型類,在類上定義的泛型,在方法中也可以使用。下面我們來看一下如何自定義泛型方法。
泛型方法不一定寫在泛型類當中。當類的調用者總是關心類中的某個泛型方法,不關心其他屬性,這個時候就沒必要再整個類上定義泛型了。
直接在方法上設置泛型(generic)
package com.caq.List;public class Generic03 { public <T> void test(T t){ System.out.println(t); } public static void main(String[] args) { Generic03 generic03 = new Generic03(); generic03.test("Monkey"); generic03.test(1); generic03.test(1.00000); generic03.test(1L); }}
運行結果:
Monkey11.01
實例中,使用<T>
來定義test
方法的泛型,它接收一個泛型的參數變量并在方法體打印;調用泛型方法也很簡單,在主方法中實例化對象,調用對象下的泛型方法,可傳入不同類型的參數。
泛型類也是一個 Java 類,它也具有繼承的特性。
泛型類的繼承可分為兩種情況:
子類明確泛型類的類型參數變量;
子類不明確泛型類的類型參數變量。
例如,有一個泛型接口:
package com.caq.List;public interface GenericInterface01<T> { default void show(T t) { }}
泛型接口的實現類如下:
package com.caq.List;public class GenericInterfaceImple implements GenericInterface01<String> { @Override public void show(String s) { System.out.println(s); }}
子類實現明確了泛型的參數變量為String
類型。因此方法show()
的重寫也將T
替換為了String
類型。
當實現類不確定泛型類的參數變量時,實現類需要定義類型參數變量,調用者使用子類時,也需要傳遞類型參數變量。
如下是GenericInterface
接口的另一個實現類:
package com.caq.List; public class GenericInterfaceImple<T> implements GenericInterface01<T> { @Override public void show(T t) { System.out.println(t); } }
在主方法中調用實現類的show()
方法:
package com.caq.List; public class GenericInterfaceImple<T> implements GenericInterface01<T> { @Override public void show(T t) { System.out.println(t); } public static void main(String[] args) { GenericInterfaceImple<Integer> integerGenericInterfaceImple = new GenericInterfaceImple<>(); integerGenericInterfaceImple.show(100); } } 100
我們先來看一個泛型作為方法參數的實例:
package com.caq.List; /** * 遍歷并打印集合中的每一個元素 * 遍歷是二叉樹上最重要的運算之一,是二叉樹上進行其它運算之基礎。 樹的遍歷是樹的一種重要的運算。 * 所謂遍歷是指對樹中所有結點的信息的訪問,即依次對樹中每個結點訪問一次且僅訪問一次。 * @param list 要接收的集合 */ public class Generic04 { public void printListElement(List<object> list){ for (Object o : list) { System.out.println(o); } } }
觀察上面的代碼,參數list
的限定的泛型類型為Object
, 也就是說,這個方法只能接收元素為Object
類型的集合,如果我們想傳遞其他元素類型的集合,是行不通的。例如,如果傳遞裝載Integer
元素的集合,程序在編譯階段就會報錯:
Tips: 泛型中的List<Object>
并不是List<Integer>
的父類,它們不滿足繼承關系。
想要解決這個問題,使用類型通配符即可,修改方法參數處的代碼,將<>
中間的Object改為?
即可:
public void printListElement(List<?> list){
此處的?
就是類型通配符,表示可以匹配任意類型,因此調用方可以傳遞任意泛型類型的列表。
實例演示
package com.caq.List; import java.util.ArrayList; import java.util.List; public class Generic04 { //List<?>可以理解為列表的類型,可以是整數型列表,也可以是字符串類型列表,list代表的是遍歷的元素代表 public void printListElement(List<?> list){ for (Object o : list) { System.out.println(o); } } public static void main(String[] args) { //實例化一個整型列表 List<Integer> interger = new ArrayList<>(); //加元素 interger.add(1); interger.add(2); interger.add(2222); //實例化對象 Generic04 generic04 = new Generic04(); generic04.printListElement(interger); //實例化一個字符串類型列表 ArrayList<String> strings = new ArrayList<>(); strings.add("element1"); strings.add("element2"); strings.add("element3"); generic04.printListElement(strings); } }
運行結果:
1
2
2222
element1
element2
element3
extends
通配符用來限定泛型的上限。什么意思呢?依舊以上面的實例為例,我們來看一個新的需求,我們希望方法接收的List
集合限定在數值類型內(float、integer、double、byte 等),不希望其他類型可以傳入(比如字符串)。此時,可以改寫上面的方法定義,設定上界通配符:
public void printListElement(List<? extends Number> list) {
這樣的寫法的含義為:List
集合裝載的元素只能是Number
自身或其子類(Number
類型是所有數值類型的父類),完整實例如下:
import java.util.ArrayList; import java.util.List; public class Generic04 { public void printListElement(List<? extends Number> list) { for (Object o : list) { System.out.println(o); } } public static void main(String[] args) { // 實例化一個整型的列表 List<Integer> integers = new ArrayList<>(); // 添加元素 integers.add(1); integers.add(2); integers.add(3); GenericDemo4 generic04 = new Generic04(); // 調用printListElement()方法 generic04.printListElement(integers); } }
運行結果:
1
2
3
既然已經了解了如何設定通配符上界,也就不難理解通配符的下界了,可以限定傳遞的參數只能是某個類型的父類。
語法如下:
<? super Type>
感謝你能夠認真閱讀完這篇文章,希望小編分享的“Java泛型是什么”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。