Java進階--泛型
1.泛型概述
泛型:是一種未知的資料型別,當我們不知道使用什麼資料型別的時候,可以使用泛型。
在學習集合時,我們都知道集合中是可以存放任意物件的,只要把物件儲存集合後,那麼這時他們都會被提升成Object型別。當我們在取出每一個物件,並且進行相應的操作,這時必須採用型別轉換。
1.1 不使用泛型的弊端
集合可以儲存任何型別的資料,這會引發異常。
大家觀察下面程式碼:
public class GenericDemo { public static void main(String[] args) { Collection coll = new ArrayList(); coll.add("abc"); coll.add("itcast"); coll.add(5); //由於集合沒有做任何限定,任何型別都可以給其中存放 Iterator it = coll.iterator(); while(it.hasNext()){ //需要列印每個字串的長度,就要把迭代出來的物件轉成String型別 String str = (String) it.next(); System.out.println(str.length()); } } }
程式在執行時發生了問題java.lang.ClassCastException,會發生型別轉換異常呢?由於集合中什麼型別的元素都可以儲存。導致取出時強轉引發執行時 ClassCastException。
1.2使用泛型的好處
- 將執行時期的ClassCastException,轉移到了編譯時期變成了編譯失敗。
- 避免了型別強轉的麻煩。
- 泛型:可以在類或方法中預支地使用未知的型別。
通過我們如下程式碼體驗一下:
public class GenericDemo2 { public static void main(String[] args) { Collection<String> list = new ArrayList<String>(); list.add("abc"); list.add("itcast"); // list.add(5);//當集合明確型別後,存放型別不一致就會編譯報錯 // 集合已經明確具體存放的元素型別,那麼在使用迭代器的時候,迭代器也同樣會知道具體遍歷元素型別 Iterator<String> it = list.iterator(); while(it.hasNext()){ String str = it.next(); //當使用Iterator<String>控制元素型別後,就不需要強轉了。獲取到的元素直接就是String型別 System.out.println(str.length()); } } }
tips:泛型是資料型別的一部分,我們將類名與泛型合併一起看做資料型別。
2.泛型的定義與使用
泛型,用來靈活地將資料型別應用到不同的類、方法、介面當中。將資料型別作為引數進行傳遞。
2.1 定義和使用含有泛型的類
定義格式:
修飾符 class 類名<代表泛型的變數> { }
例如,API中的ArrayList集合:
class ArrayList<E>{
public boolean add(E e){ }
public E get(int index){ }
....
}
使用泛型: 即什麼時候確定泛型。
在建立物件的時候確定泛型
例如,
ArrayList<String> list = new ArrayList<String>();
此時,變數E的值就是String型別,那麼我們的型別就可以理解為:
class ArrayList<String>{
public boolean add(String e){ }
public String get(int index){ }
...
}
再例如,ArrayList<Integer> list = new ArrayList<Integer>();
此時,變數E的值就是Integer型別,那麼我們的型別就可以理解為:
class ArrayList<Integer> {
public boolean add(Integer e) { }
public Integer get(int index) { }
...
}
舉例自定義泛型類
public class MyGenericClass<MVP> {
//沒有MVP型別,在這裡代表 未知的一種資料型別 未來傳遞什麼就是什麼型別
private MVP mvp;
public void setMVP(MVP mvp) {
this.mvp = mvp;
}
public MVP getMVP() {
return mvp;
}
}
使用:
public class GenericClassDemo {
public static void main(String[] args) {
// 建立一個泛型為String的類
MyGenericClass<String> my = new MyGenericClass<String>();
// 呼叫setMVP
my.setMVP("大鬍子登登");
// 呼叫getMVP
String mvp = my.getMVP();
System.out.println(mvp);
//建立一個泛型為Integer的類
MyGenericClass<Integer> my2 = new MyGenericClass<Integer>();
my2.setMVP(123);
Integer mvp2 = my2.getMVP();
}
}
2.2 含有泛型的方法
定義格式:
修飾符 <代表泛型的變數> 返回值型別 方法名(引數){ }
例如,
public class MyGenericMethod {
public <MVP> void show(MVP mvp) {
System.out.println(mvp.getClass());
}
public <MVP> MVP show2(MVP mvp) {
return mvp;
}
}
使用格式:呼叫方法時,確定泛型的型別
public class GenericMethodDemo {
public static void main(String[] args) {
// 建立物件
MyGenericMethod mm = new MyGenericMethod();
// 演示看方法提示
mm.show("aaa");
mm.show(123);
mm.show(12.45);
}
}
2.3 含有泛型的介面
定義格式:
修飾符 interface介面名<代表泛型的變數> { }
例如,
public interface MyGenericInterface<E>{
public abstract void add(E e);
public abstract E getE();
}
使用格式:
1、定義類時確定泛型的型別
例如
public class MyImp1 implements MyGenericInterface<String> {
@Override
public void add(String e) {
// 省略...
}
@Override
public String getE() {
return null;
}
}
此時,泛型E的值就是String型別。
2、始終不確定泛型的型別,直到建立物件時,確定泛型的型別
例如
public class MyImp2<E> implements MyGenericInterface<E> {
@Override
public void add(E e) {
// 省略...
}
@Override
public E getE() {
return null;
}
}
確定泛型:
/*
* 使用
*/
public class GenericInterface {
public static void main(String[] args) {
MyImp2<String> my = new MyImp2<String>();
my.add("aa");
}
}
3.泛型萬用字元
當使用泛型類或者介面時,傳遞的資料中,泛型型別不確定,可以通過萬用字元<?>表示。但是一旦使用泛型的萬用字元後,只能使用Object類中的共性方法,集合中元素自身方法無法使用。
萬用字元基本使用
泛型的萬用字元:不知道使用什麼型別來接收的時候,此時可以使用?,?表示未知萬用字元。
此時只能接受資料,不能往該集合中儲存資料。
舉個例子大家理解使用即可:
package 集合和泛型.Generic;
import java.util.ArrayList;
import java.util.Iterator;
public class Demo05Generic {
public static void main(String[] args) {
ArrayList<Integer> lsit01 = new ArrayList<>();
lsit01.add(1);
lsit01.add(2);
lsit01.add(3);
ArrayList<String> lsit02 = new ArrayList<>();
lsit02.add("a");
lsit02.add("b");
lsit02.add("c");
PrintArray(lsit01);
PrintArray(lsit02);
// ArrayList<?> lsit03 = new ArrayList<?>(); 定義不能使用萬用字元
}
// 定義一個方法,能遍歷所有的ArrayList集合
public static void PrintArray(ArrayList<?> list){
Iterator<?> it = list.iterator();
while (it.hasNext()){
Object obj = it.next();
System.out.println(obj);
}
}
}
tips:泛型不存在繼承關係
萬用字元高階使用----受限泛型
之前設定泛型的時候,實際上是可以任意設定的,只要是類就可以設定。但是在JAVA的泛型中可以指定一個泛型的上限和下限。
泛型的上限:
- 格式:
型別名稱 <? extends 類 > 物件名稱
- 意義:
只能接收該型別及其子類
泛型的下限:
- 格式:
型別名稱 <? super 類 > 物件名稱
- 意義:
只能接收該型別及其父型別
比如:現已知Object類,String 類,Number類,Integer類,其中Number是Integer的父類
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>();
getElement(list1);
getElement(list2);//報錯
getElement(list3);
getElement(list4);//報錯
getElement2(list1);//報錯
getElement2(list2);//報錯
getElement2(list3);
getElement2(list4);
}
// 泛型的上限:此時的泛型?,必須是Number型別或者Number型別的子類
public static void getElement1(Collection<? extends Number> coll){}
// 泛型的下限:此時的泛型?,必須是Number型別或者Number型別的父類
public static void getElement2(Collection<? super Number> coll){}