2022-12-29
泛型 類型
泛型類就是在類聲明時(shí)通過一個(gè)標(biāo)識(shí)表示類中某個(gè)屬性的類型或者是某個(gè)方法的返回值及參數(shù)類型。只有用戶使用該類的時(shí)候,該類所屬的類型才能明確。
泛型類的聲明格式具體如下:
[訪問權(quán)限] class 類名稱<泛型類型標(biāo)識(shí)1, 泛型類型標(biāo)識(shí)2, ..., 泛型類型標(biāo)識(shí)n> {
[訪問權(quán)限] 泛型類型標(biāo)識(shí) 變量名稱;
[訪問權(quán)限] 泛型類型標(biāo)識(shí) 方法名稱(參數(shù)){};
[訪問權(quán)限] 返回值類型聲明 方法名稱(泛型類型標(biāo)識(shí) 變量名稱){};
}
定義好泛型類之后,就可以創(chuàng)建泛型對(duì)象。創(chuàng)建泛型對(duì)象的語法格式具體如下:
類名稱<參數(shù)化類型> 對(duì)象名稱 = new 類名稱<參數(shù)化類型>();
為了大家更好地理解泛型類以及泛型對(duì)象的使用,下面先看一個(gè)例子。
import java.util.ArrayList;
public class Example22 {
public static void main(String[] args) {
ArrayList list = new ArrayList(); // 創(chuàng)建ArrayList集合
list.add("abc"); // 添加字符串對(duì)象
list.add(10); // 添加Integer對(duì)象
list.add(20);
for (Object obj : list) { // 遍歷集合
String str = (String) obj; // 強(qiáng)制轉(zhuǎn)換成String類型
}
}
}
在上述代碼中,向List集合存入了3個(gè)元素,分別是一個(gè)字符串和兩個(gè)int類型的整數(shù)。在取出這些元素時(shí),都將它們強(qiáng)轉(zhuǎn)為String類型,由于Integer對(duì)象無法轉(zhuǎn)換為String類型,因此程序在運(yùn)行時(shí)會(huì)出現(xiàn)java.lang.ClassCastException的異常。
接下來,對(duì)上述代碼進(jìn)行修改,如下所示:
ArrayListlist = new ArrayList();
上面這種寫法就限定了ArrayList集合只能存儲(chǔ)Integer類型元素,改寫后的程序在中編譯時(shí)就會(huì)出現(xiàn)錯(cuò)誤提示。修改之后的程序編譯報(bào)錯(cuò)的原因是修改后的代碼限定了集合元素的數(shù)據(jù)類型,ArrayList這樣的集合只能存儲(chǔ)Integer類型的元素,程序在編譯時(shí),編譯器檢查出String類型的元素與List集合的規(guī)定類型不匹配,編譯不通過。
使用泛型可以很好的解決上述問題。接下來,使用泛型再次對(duì)上述案例進(jìn)行改寫。
import java.util.ArrayList;
public class Example23 {
public static void main(String[] args) {
ArrayListlist = new ArrayList(); // 創(chuàng)建ArrayList集合
list.add(10); // 添加Integer對(duì)象
list.add(20);
for (Integer ele : list) { // 遍歷集合
System.out.println(ele);
}
}
}
上述代碼從運(yùn)行結(jié)果可以看出,該文件已經(jīng)可以正常運(yùn)行。需要注意的是,在使用泛型后,每次遍歷集合元素時(shí),可以指定元素類型為Integer,而不是Object,這樣就避免了在程序中進(jìn)行強(qiáng)制類型轉(zhuǎn)換。
6.3 泛型方法
泛型方法的定義與其所在的類是否是泛型類是沒有任何關(guān)系的,泛型方法所在的類可以是泛型類,也可以不是泛型類。定義泛型方法代碼如下所示:
[訪問權(quán)限] <泛型標(biāo)識(shí)> 返回值類型 方法名稱(泛型標(biāo)識(shí) 參數(shù)名稱) {}
接下來通過一個(gè)案例來學(xué)習(xí)泛型方法的定義與使用。
class Dog {
String eat;
Integer age;
publicvoid show(T t) {
System.out.println(t);
}
}
public class Example24 {
public static void main(String[] args) {
Dog dog = new Dog(); // 創(chuàng)建對(duì)象
//調(diào)用方法,傳入的參數(shù)是什么類型,返回值就是什么類型
dog.show("hello");
dog.show(12);
dog.show(12.5);
}
}
上述代碼中,定義了一個(gè)泛型方法show(),并將show()方法的參數(shù)類型和返回值類型規(guī)定為泛型,這樣調(diào)用方法時(shí),傳入的參數(shù)是什么類型,返回值就是什么類型,如果定義為其他類型,傳入?yún)?shù)就必須是方法指定的參數(shù)類型,否則編譯就會(huì)出現(xiàn)錯(cuò)誤。
6.4 泛型接口
在JDK5之后,不僅可以聲明泛型類,也可以聲明泛型接口,聲明泛型接口和聲明泛型類的語法類似,也是在接口名稱后面加上,聲明泛型接口的格式如下所示。
[訪問權(quán)限] interface 接口名稱<泛型標(biāo)識(shí)> {}
利用以上格式定義一個(gè)泛型接口,如下所示。
interface MyInter{
T getVar();
}
泛型接口定義完成之后,就要定義此接口的子類,定義泛型接口的子類有兩種方式,一種是直接在子類實(shí)現(xiàn)的接口中明確地給出泛型類型,另一種是直接在子類后聲明泛型。
6.4.1 直接在接口中指定具體類型
當(dāng)子類明確泛型類的類型參數(shù)變量時(shí),外界使用子類的時(shí)候,需要傳遞類型參數(shù)變量進(jìn)來,在實(shí)現(xiàn)類中需要定義出類型參數(shù)變量。接下來通過一個(gè)案例學(xué)習(xí)這種情況的泛型接口定義。
1.首先定義一個(gè)泛型接口。
public interface MyInter{
void show(T t);
}
2.接著定義泛型接口的子類。
Java
public class MyInnerImpl implements MyInter{
@Override
public void show(String s) {
System.out.println(s);
}
}
3.最后定義實(shí)現(xiàn)類,進(jìn)行測試。
public class Example25 {
public static void main(String[] args) {
MyIntermyInter = new MyInnerImpl();
myInter.show("Hello World");
}
}
上述代碼中定義了一個(gè)泛型接口MyInter,在泛型接口子類MyInterImpl中實(shí)現(xiàn)了MyInter接口。MyInterImpl實(shí)現(xiàn)MyInter接口時(shí),直接在實(shí)現(xiàn)的接口處指定了具體的泛型類型String,這樣在重寫MyInter接口中的show()方法時(shí)直接指明類型為String即可。
6.4.2 在子類的定義上聲明泛型類型
當(dāng)子類不明確泛型類的類型參數(shù)變量,外界使用子類的時(shí)候,也需要傳遞類型參數(shù)變量進(jìn)來,在實(shí)現(xiàn)類中也需要定義出類型參數(shù)變量。接下來通過修改子類MyInnerImpl和測試程序來學(xué)習(xí)這種情況的泛型接口定義。
1.泛型接口子類MyInnerImpl的實(shí)現(xiàn)如下。
public class MyInnerImplimplements MyInter{
@Override
public void show(T t) {
System.out.println(t);
}
}
2.在Example25類中測試代碼實(shí)現(xiàn)如下。
public class Example25 {
public static void main(String[] args) {
// ...
MyIntermyInt = new MyInnerImpl<>();
myInt.show(20);
}
}
對(duì)比上述案例可知,當(dāng)子類不確定泛型類的類型參數(shù)變量時(shí),在定義對(duì)象時(shí)泛型可以為任意類型。
6.5 類型通配符
在Java中,數(shù)組是可以協(xié)變的,例如,Dog extends Animal,那么Animal[]與dog[]是可以兼容的。而集合是不能協(xié)變的,也就是說List不是List的父類, 為了解決這個(gè)問題,Java泛型為我們提供了類型通配符“?”。
下面我們通過一個(gè)案例演示通配符的使用。
import java.util.ArrayList;
import java.util.List;
public class Example26 {
public static void main(String[] args) {
// List集合裝載的是Integer
Listlist = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
test(list);
}
public static void test(List list) {
for(int i = 0; i<list.size(); p="" {<="" i++)="">
System.out.println(list.get(i));
}
}
}
需要注意的是,如果使用通配符“?”接收泛型對(duì)象,則通配符“?”修飾的對(duì)象只能接收,不能修改,也就是不能設(shè)置。下述代碼編譯時(shí)編譯器會(huì)報(bào)錯(cuò)。
import java.util.ArrayList;
import java.util.List;
public class Example27 {
public static void main(String[] args) {
List list = new ArrayList();
list.add("張三");
// list.add(null);
}
}
由于以上程序?qū)⒁粋€(gè)字符串設(shè)置給泛型所聲明的屬性,因?yàn)槭褂肔ist的形式,所以無法將內(nèi)容添加到集合中,但可以設(shè)置為null值。因此編譯上述代碼,編譯器會(huì)報(bào)錯(cuò),錯(cuò)誤描述見下。
java: 不兼容的類型: java.lang.String無法轉(zhuǎn)換為capture#1, 共 ?
通配符表示可以匹配任意類型,任意的Java類都可以匹配,但是當(dāng)接收一個(gè)List集合時(shí),它只能操作數(shù)字類型的元素(Float、Integer、Double、Byte等數(shù)字類型都行),而如果直接使用通配符的話,該集合就不是只能操作數(shù)字了。針對(duì)這類問題我們可以設(shè)定通配符的上限和下限。
設(shè)定通配符上限代碼如下所示:
List<? extends Number>
設(shè)定通配符下限代碼如下所示:
<? super Type>
開班時(shí)間:2021-04-12(深圳)
開班盛況開班時(shí)間:2021-05-17(北京)
開班盛況開班時(shí)間:2021-03-22(杭州)
開班盛況開班時(shí)間:2021-04-26(北京)
開班盛況開班時(shí)間:2021-05-10(北京)
開班盛況開班時(shí)間:2021-02-22(北京)
開班盛況開班時(shí)間:2021-07-12(北京)
預(yù)約報(bào)名開班時(shí)間:2020-09-21(上海)
開班盛況開班時(shí)間:2021-07-12(北京)
預(yù)約報(bào)名開班時(shí)間:2019-07-22(北京)
開班盛況Copyright 2011-2023 北京千鋒互聯(lián)科技有限公司 .All Right 京ICP備12003911號(hào)-5 京公網(wǎng)安備 11010802035720號(hào)