天天看點

JavaSE---泛型

文章目錄

    • 泛型是什麼?
    • 泛型對比測試
    • 為什麼要使用泛型?
    • 使用泛型有什麼好處?
    • 怎麼使用泛型?
      • 泛型類
      • 泛型方法
      • 泛型類派生出的子類
      • 類型通配符
      • 泛型擦除
    • 應用場景有哪些?

泛型是什麼?

  • JDK1.5,引進泛型這個概念。
  • 泛型: 在建立對象或者調用方法的時候,再去明确類型。即參數化類型,簡單來說,就是把類型當作一種參數來傳遞,隻不過一般情況下參數寫在()中,而類型寫在<>中。并且在<>中隻能是引用類型。
  • 引用類型(reference type): 指向一個對象,而不是原始值,指向對象的變量是引用變量。在java裡面除去基本資料類型的其它類型都是引用資料類型,自己定義的class類都是引用類型,可以像基本類型一樣使用。
  • 泛型的設計原則: 隻要在編譯時期沒有出現警告,那麼運作時期就不會出現

    ClassCastException

    異常。

泛型對比測試

  • 首先來看不加泛型的情況下,在list集合中添加不同類型的資料,并且在周遊的時候強轉,編譯的時候注定會報

    ClassCastException

    異常的。這就是由于集合中元素類型不統一造成的。
List arrayList = new ArrayList();
//添加一個String類型的元素
arrayList.add("aaaa");
//添加一個Integer類型的元素
arrayList.add(100);

for(int i = 0; i< arrayList.size();i++){
	//都強轉成String類型
    String item = (String)arrayList.get(i);
    Log.d("泛型測試","item = " + item);
}
           
  • 加上泛型之後的,就規定了,這個集合中隻能裝這個類型的元素,要是裝别的,在敲代碼的時候就會報錯,因為你加入的元素,跟規定的類型不一緻,不讓你加。

為什麼要使用泛型?

  • 早期用Object來代替任意類型,但是這樣做,有的時候就要進行向下強轉,這樣做不太安全。
  • 沒有泛型的話,像

    Collection

    Map

    這種不限制元素類型的集合,你可以往裡面丟任何元素,并且不會報文法錯誤,但是集合不知道這個元素是什麼類型的,預設都是

    Object

    類型,等你取出來的時候,就給你返

    Object

    類型的,可謂亂丢一時爽,

    get

    火葬場。
  • 有了泛型呢,就不用強制轉換了,因為你在事先就規定好了,這個集合中裝什麼類型的元素,使代碼更加簡潔。程式也更加健壯(這才是猛男該做得事),因為隻要編譯沒有警告,那麼運作的時候就不會出現

    ClassCastException

    異常了。

使用泛型有什麼好處?

  • 上面通過對比已經看出,泛型有哪些好處,這裡在總結一下。

    ①代碼更簡潔

    ②程式更健壯

    ③可讀性和穩定性

    ④配合增強for循環周遊集合

怎麼使用泛型?

泛型類

  • 把泛型定義在類上,也可以定義在方法上,在使用該類或者方法的時候,才根據自己的需要,将類型明确下來。注意: 類上聲明的泛型,隻對非靜态成員有效。
1.定義
//把泛型定義在類上
public class ObjectTool<T>{
	private object;
	
	public T getObj(){
	return object;
	}
	
	public void setObj(T object){
	this.object=object;
	}
}
           
2.使用
public static void main(String[] args){
	//建立對象,并指定其元素類型(這裡是String型)
	ObjectTool<String> tool1 = new ObjectTool<>();
	
	tool1.setObj(new String("張三"));
	String s=tool.getObj();
	System.Out.println(s);
	
	//建立對象,并指定其元素類型(這裡是Integer型)
	ObjectTool<Integer> tool2 = new ObjectTool<>();
	
	tool2.setObj(10);
	int i=tool2.getObj();
	System.Out.println(i);
}
           

泛型方法

  • 如果外界僅僅對一個方法感興趣,而不關心類中的其他屬性,那麼将泛型定義在類上就有些小題大做,這裡直接定義在方法上,精準打擊!!
1.定義泛型方法
	public <T> void show(T t){
	System.Out.println(t);
	}
           
2.泛型方法的使用
public static void main (String[] args){
	//建立對象
	ObjectTool obj = new ObjectTool();

	//調用方法,傳進來什麼類型,傳回值就是什麼類型
	obj.show("hello");
	obj.show(4);
	obj.show(false);
}
           

泛型類派生出的子類

  • 換上馬甲他還是王八,雖然加了泛型,但說到底,他還是個類,既然是個類,那麼他就可以被繼承。然而泛型類的繼承也分兩種。

①子類明确泛型類的類型參數變量(泛型接口)

//把泛型定義在接口上
public interface Inter<T> {
    public abstract void show(T t);
}
           
//子類明确泛型類的類型參數變量:
public class InterImpl implements Inter<String> {
    @Override
    public void show(String s) {
        System.out.println(s);
    }
}
           

②子類不明确泛型類的類型參數變量

  • 此時,外界使用子類的時候,也需要傳遞類型參數變量進來,在實作類上需要定義出類型參數變量。
//實作類要定義出<T>類型
public class InterImpl<T> implements Inter<T> {

    @Override
    public void show(T t) {
        System.out.println(t);
    }
}
           

類型通配符

  • 問題: 方法接收一個集合參數,周遊集合并把集合元素列印出來
普通解決辦法:
public void test(List list){
    for(int i=0;i<list.size();i++){
        System.out.println(list.get(i));
    }
}
           
加類型通配符?
public void test(List<?> list){
    for(int i=0;i<list.size();i++){
        System.out.println(list.get(i));
    }
}
           
  • 就加了一個問号?,能有什麼作用

    答案是:更優雅~

  • 還有一個問題是設定通配符上限和下限。這個問題很繞,搞不清楚,當然,大多時候,使用泛型方法來替代通配符,條條大路通羅馬嘛。
  • 注意:加了?就隻能調用與對象無關的方法,不能調用對象與類型有關的方法。因為直到外界使用才知道具體的類型是什麼。也就是說,在上面的List集合,是不能使用add()方法的。因為add()方法是把對象丢進集合中,而現在不知道對象的類型。

泛型擦除

  • 因為泛型是提供給javac編譯器使用的,它用于限定集合的輸入類型,讓編譯器在源代碼級别上,即擋住向集合中插入非法資料。但編譯器編譯完帶有泛形的java程式後,生成的class檔案中将不再帶有泛形資訊,以此使程式運作效率不受到影響,這個過程稱之為“泛型擦除”。這一點應用在相容老版本上,因為JDK1.5之前沒有泛型,當把帶有泛型特性的集合指派給老版本的集合時候,就會把泛型擦除掉,amazing!

應用場景有哪些?

  • 場景一: 最常用的還是應用于限制集合的參數類型。
  • 場景二: 項目中,每次都要寫好幾個DAO,會顯得臃腫。
  • 優化辦法–抽象DAO: 在抽象DAO中,是不知道哪一個DAO會繼承它,是以是不知道其具體的類型的。而泛型就是在建立的時候才指定其具體的類型。
抽象DAO
public abstract class BaseDao<T> {
    private Session session;
    private Class clazz;

    //哪個子類調的這個方法,得到的class就是子類處理的類型(非常重要)
    public BaseDao(){
        Class clazz = this.getClass();  //拿到的是子類
        ParameterizedType  pt = (ParameterizedType) clazz.getGenericSuperclass(); 
         //BaseDao<Category>
        clazz = (Class) pt.getActualTypeArguments()[0];
        System.out.println(clazz);
    }

    public void add(T t){
        session.save(t);
    }

    public T find(String id){
        return (T) session.get(clazz, id);
    }

    public void update(T t){
        session.update(t);
    }

    public void delete(String id){
        T t = (T) session.get(clazz, id);
        session.delete(t);
    }

}
           
繼承抽象DAO
public class CategoryDao extends BaseDao<Category> {
	将抽象DAO中的方法繼承了下來
	@override
	...
	...
	...
	}
	
public class BookDao extends BaseDao<Book> {
	@override
	...
	...
	...
	}