一、什么是泛型?
1.泛型是一種未知的數(shù)據(jù)類型,當(dāng)我們不知道使用什么數(shù)據(jù)類型的時(shí)候,可以使用泛型。
2.泛型也可以看作是一個(gè)變量,用來(lái)接收數(shù)據(jù)類型(注意接收的是數(shù)據(jù)類型)。
E e:Element 元素
T t:Type 類型
例如:ArrayList集合在定義的時(shí)候,不知道集合中都會(huì)存儲(chǔ)什么類型的數(shù)據(jù),所以類型使用泛型。
public class ArrayList<E> {
public boolean add(E e) {}
public E get(int index){}
}
創(chuàng)建集合對(duì)象的時(shí)候,就會(huì)確定泛型的數(shù)據(jù)類型

二、泛型的好處
創(chuàng)建集合對(duì)象,使用泛型
好處:
- 避免了類型轉(zhuǎn)換的麻煩,存儲(chǔ)的是什么類型,取出的就是什么類型;
- 把運(yùn)行期異常(代碼運(yùn)行之后會(huì)拋出的異常),提升到了編譯期(寫(xiě)代碼的時(shí)候會(huì)報(bào)錯(cuò));
弊端:
- 泛型是什么類型,只能存儲(chǔ)什么類型的數(shù)據(jù);
private static void show01() {
ArrayList<String> list = new ArrayList<>();
list.add("abc");
list.add(1);//報(bào)錯(cuò):add(java.lang.String)in ArrayList cannot be applied to (int)
//使用迭代器遍歷list集合
Iterator<String> it = list.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s+"->"+s.length());
}
}
創(chuàng)建集合對(duì)象,不使用泛型
好處: 集合不使用泛型,默認(rèn)的類型就是Object類型,可以存儲(chǔ)任意類型的數(shù)據(jù);
弊端: 不安全,會(huì)引發(fā)異常;
private static void show02() {
ArrayList list = new ArrayList();
list.add("abc");
list.add(1);
//使用迭代器遍歷list集合
//獲取迭代器
Iterator it = list.iterator();
//使用迭代器中的方法hasNext和next遍歷集合
while(it.hasNext()){
//取出元素也是Object類型
Object obj = it.next();
System.out.println(obj);
//想要使用String類特有的方法,length獲取字符串的長(zhǎng)度;不能使用 多態(tài) Object obj = "abc";
//需要向下轉(zhuǎn)型
//會(huì)拋出ClassCastException類型轉(zhuǎn)換異常,不能把Integer類型轉(zhuǎn)換為String類型
String s = (String)obj;
System.out.println(s.length());
}
}
三、泛型的定義與使用
1.定義使用含有泛型的類
/**
定義一個(gè)含有泛型的類,模擬ArrayList集合
泛型是一個(gè)未知的數(shù)據(jù)類型,當(dāng)我們不確定什么什么數(shù)據(jù)類型的時(shí)候,可以使用泛型
泛型可以接收任意的數(shù)據(jù)類型,可以使用Integer,String,對(duì)象Student...
創(chuàng)建對(duì)象的時(shí)候確定泛型的數(shù)據(jù)類型
*/
public class GenericClass<E>{
private E name;
public E getName() {
return name;
}
public void setName(E name) {
this.name = name;
}
}
使用該類
public class DemoGenericClass {
public static void main(String[] args) {
//不寫(xiě)泛型默認(rèn)為Object類型
GenericClass gc = new GenericClass();
gc.setName("只能是字符串");
Object obj = gc.getName();
System.out.println(obj);
GenericClass<Integer> gc2 = new GenericClass<>();
gc2.setName(1);
Integer name1 = gc2.getName();
System.out.println(name1);
//創(chuàng)建GenericClass對(duì)象,泛型使用String類型
GenericClass<String> gc3 = new GenericClass<>();
gc3.setName("小明");
String name2 = gc3.getName();
System.out.println(name2);
}
}
2.定義和使用含有泛型的方法
泛型定義在方法的修飾符和返回值類型之間
格式:
修飾符 <泛型> 返回值類型 方法名(泛型 參數(shù)名){
? 方法體;
}
含有泛型的方法,在調(diào)用方法的時(shí)候確定泛型的數(shù)據(jù)類型
傳遞什么類型的參數(shù),泛型就是什么類型
public class GenericMethod {
//定義一個(gè)含有泛型的方法
public <M> void method01(M m) {
System.out.println(m);
}
//定義一個(gè)含有泛型的靜態(tài)方法
public static <S> void method02(S s){
System.out.println(s);
}
//返回值泛型
public <T> T method03(T t) {
return t;
}
}
使用該類
public class DemoGenericMethod {
public static void main(String[] args) {
//創(chuàng)建GenericMethod對(duì)象
GenericMethod gm = new GenericMethod();
/*
調(diào)用含有泛型的方法method01
傳遞什么類型,泛型就是什么類型
*/
gm.method01(10);
gm.method01("abc");
gm.method01(8.8);
gm.method01(true);
//靜態(tài)方法,通過(guò)類名.方法名(參數(shù))可以直接使用
GenericMethod.method02("靜態(tài)方法");
GenericMethod.method02(1);
///泛型返回值
String s = gm.method03("aaa");
System.out.println(s);
}
}
3.定義和使用含有泛型的接口
/**
* 定義含有泛型的接口
*/
public interface GenericInterface<I> {
public abstract void method(I i);
}
含有泛型的接口第一種使用方式:定義接口的實(shí)現(xiàn)類,實(shí)現(xiàn)接口,指定接口的泛型
public class GenericInterfaceImpl1 implements GenericInterface<String> {
@Override
public void method(String s) {
System.out.println(s);
}
}
public class DemoGenericInterface {
public static void main(String[] args) {
//創(chuàng)建GenericInterfaceImpl1對(duì)象
GenericInterfaceImpl1 gi1 = new GenericInterfaceImpl1();
gi1.method("字符串");
}
}
含有泛型的接口第二種使用方式:接口使用什么泛型,實(shí)現(xiàn)類就使用什么泛型,類跟著接口走
/**
* 含有泛型的接口第二種使用方式:接口使用什么泛型,實(shí)現(xiàn)類就使用什么泛型,類跟著接口走
* 就相當(dāng)于定義了一個(gè)含有泛型的類,創(chuàng)建對(duì)象的時(shí)候確定泛型的類型
* public interface List<E>{
* boolean add(E e);
* E get(int index);
* }
* public class ArrayList<E> implements List<E>{
* public boolean add(E e) {}
* public E get(int index) {}
* }
*/
public class GenericInterfaceImpl2<I> implements GenericInterface<I>{
@Override
public void method(I i) {
System.out.println(i);
}
}
public class DemoGenericInterface {
public static void main(String[] args) {
//創(chuàng)建GenericInterfaceImpl2對(duì)象
GenericInterfaceImpl2<Integer> gi2 = new GenericInterfaceImpl2<>();
gi2.method(10);
GenericInterfaceImpl2<Double> gi3 = new GenericInterfaceImpl2<>();
gi3.method(8.8);
}
}
四、泛型通配符
1.泛型通配符的使用
當(dāng)使用泛型類或者接口時(shí),傳遞的數(shù)據(jù)中,泛型不確定,可以通過(guò)通配符 < ? > 表示。但是一旦使用泛型通配符后,只能使用Object類中的共性方法,集合中元素自身的方法無(wú)法使用。
泛型的通配符:
??:代表任意的數(shù)據(jù)類型
使用方式:
?1.不能創(chuàng)建對(duì)象使用
?2.只能作為方法的參數(shù)使用
/**
* 泛型的通配符:
* ?:代表任意的數(shù)據(jù)類型
* 使用方式:
* 不能創(chuàng)建對(duì)象使用
* 只能作為方法的參數(shù)使用
*/
public class DemoGeneric {
public static void main(String[] args) {
ArrayList<Integer> list01 = new ArrayList<>();
list01.add(1);
list01.add(2);
ArrayList<String> list02 = new ArrayList<>();
list02.add("a");
list02.add("b");
printArray(list01);
printArray(list02);
//ArrayList<?> list03 = new ArrayList<?>();//報(bào)錯(cuò):不能創(chuàng)建對(duì)象使用
}
/**
* 定義一個(gè)方法,能遍歷所有類型的ArrayList集合
* 這時(shí)候我們不知道ArrayList集合使用什么數(shù)據(jù)類型,可以泛型的通配符?來(lái)接收數(shù)據(jù)類型
* 注意:
* 泛型沒(méi)有繼承概念的
* @param list
*/
public static void printArray(ArrayList<?> list) {
Iterator<?> it = list.iterator();
while (it.hasNext()) {
//it.next()方法,取出的元素是Object,可以接收任意的數(shù)據(jù)類型
Object o = it.next();
System.out.println(o);
}
}
}
2.泛型通配符的高級(jí)使用----受限泛型
之前設(shè)置泛型的時(shí)候,實(shí)際上可以是任意設(shè)置的,只要是類就可以設(shè)置。但是在Java中泛型可以指定一個(gè)泛型的上限和下限。
泛型的上限
- 格式:類型名稱 <? extends 類 > 對(duì)象名稱
- 意義:只接收該類型及其子類
泛型的下限
- 格式:類型名稱 <? super 類 > 對(duì)象名稱
- 意義:只接收該類型及其父類
/*
泛型的上限限定: ? extends E 代表使用的泛型只能是E類型的子類/本身
泛型的下限限定: ? super E 代表使用的泛型只能是E類型的父類/本身
*/
public class Demo06Generic {
public static void main(String[] args) {
Collection<Integer> list1 = new ArrayList<Integer>();
Collection<String> list2 = new ArrayList<String>();
Collection<Number> list3 = new ArrayList<Number>();
Collection<Object> list4 = new ArrayList<Object>();
getElement1(list1);
//getElement1(list2);//報(bào)錯(cuò)
getElement1(list3);
//getElement1(list4);//報(bào)錯(cuò)
//getElement2(list1);//報(bào)錯(cuò)
//getElement2(list2);//報(bào)錯(cuò)
getElement2(list3);
getElement2(list4);
/*
類與類之間的繼承關(guān)系
Integer extends Number extends Object
String extends Object
*/
}
// 泛型的上限:此時(shí)的泛型?,必須是Number類型或者Number類型的子類
public static void getElement1(Collection<? extends Number> coll){}
// 泛型的下限:此時(shí)的泛型?,必須是Number類型或者Number類型的父類
public static void getElement2(Collection<? super Number> coll){}
}
以上是我的整理,希望能幫助大家,謝謝。
最后
歡迎關(guān)注公眾號(hào):前程有光,領(lǐng)取一線大廠Java面試題總結(jié)+各知識(shí)點(diǎn)學(xué)習(xí)思維導(dǎo)+一份300頁(yè)pdf文檔的Java核心知識(shí)點(diǎn)總結(jié)! 這些資料的內(nèi)容都是面試時(shí)面試官必問(wèn)的知識(shí)點(diǎn),篇章包括了很多知識(shí)點(diǎn),其中包括了有基礎(chǔ)知識(shí)、Java集合、JVM、多線程并發(fā)、spring原理、微服務(wù)、Netty 與RPC 、Kafka、日記、設(shè)計(jì)模式、Java算法、數(shù)據(jù)庫(kù)、Zookeeper、分布式緩存、數(shù)據(jù)結(jié)構(gòu)等等。