圖文詳解 Java 泛型,寫得太好了!

來源:iyu77.blog.csdn.net/article/details/125304857


泛型—— 一種可以接收數據類型的數據類型,本文將通俗講解 Java 泛型的優點、方法及相關細節。

一、泛型的引入

我們都知道,繼承是面向對象的三大特性之一,比如在我們向集合中添加元素的過程中 add() 方法裏填入的是 Object 類,而 Object 又是所有類的父類,這就產生了一個問題——添加的類型無法做到統一 由此就可能產生在遍歷集合取出元素時類型不統一而報錯問題。

例如:我向一個 ArrayList 集合中添加 Person 類的對象,但是不小心手賤添加了一個 Boy 類的對象,這就會導致如下結果

傳統的方式不能對加入到集合 ArrayList 中的數據類型進行約束 (不安全) 遍歷的時候,需要進行類型轉換, 如果集合中的數據量較大,對效率有影響 這就極大地降低了程序的健壯性,因此設計者針對此問題引入了泛型!

二、使用泛型的好處

  1. 提升了程序的健壯性和規範性

針對上述問題,當我們採用泛型就會顯得非常簡單,只需要在編譯類型後利用泛型指定一個特定類型,編譯器就會自動檢測出不符合規範的類並拋出錯誤提示

  1. 編譯時, 檢查添加元素的類型,提高了安全性

  2. 減少了類型轉換的次數, 提高效率

  1. 在類聲明時通過一個標識可以表示屬性類型、方法的返回值類型、參數類型
class Person<E> {
    E s;   //可以是屬性類型
    public Person(E s) {  //可以是參數類型
        this.s = s;
    }
    public E f() { //可以是返回類型
        return s;
    }
    public void show() {
        System.out.println(s.getClass());  //顯示S的運行類型
    }
}

可以這樣理解:上述的 class Person中的 “E” 相當於這裏的 E 是一個軀殼 佔位用的 以後定義的時候程序員可以自己去自定義。

就像這樣

public static void main(String[] args) {
    Person<String> person1 = new Person<String>("xxxx");// E->String
    person.show();
    Person<Integer> person2 = new Person<Integer>(123); // E->Integer
    person.show();
}
運行結果:
class java.lang.String
class java.lang.Integer

三、泛型常見用法

1. 定義泛型接口

曾經寫接口的時候都沒有定義泛型,它默認的就是 Object 類,其實這樣寫是不規範的!

如果說接口的存在是一種規範,那泛型接口就是規範中的規範

interface Im<U,R>{
    void hi(R r);
    void hello(R r1,R r2,U u1,U u2);
    default R method(U u){
        return null;
    }
}

在上述的泛型接口中已經規定傳入其中的必須是 U,R 類的對象,那麼當我們傳入其他類的對象時就會報錯,如圖:

根據規則,當我們實現接口時,就必須實現他的所有方法,而在這時我們就可以向 <U,R> 中傳入我們自己規定的類。在 IDEA 中重寫接口中的方法時,編譯器會自動將 < U,R > 替換成我們事先規定的類。

2. 定義泛型集合

  1. 使用泛型方式給 HashSet 中放入三個學生對象,並輸出對象信息
 HashSet<Student> students = new HashSet<Student>();
 students.add(new Student("懶羊羊",21));
 students.add(new Student("喜羊羊",41));
 students.add(new Student("美羊羊",13));
 for (Student student :students) {
     System.out.println(student);
 }

上述的 泛型中 Student 的是我事先定義好的一個類,把它放到其中作爲泛型來約束傳入的對象,以及在遍歷時減少轉型的次數提高效率

  1. 使用泛型方式給 HashMap 中放入三個學生對象,並輸出對象信息
HashMap<String, Student> hm = new HashMap<String, Student>();
// K-> String  V->Student與下面的對應
hm.put("001",new Student("喜羊羊",21));
hm.put("002",new Student("懶羊羊",32));
hm.put("003",new Student("美羊羊",43));
Set<Map.Entry<String,Student>> ek=hm.entrySet();
Iterator<Map.Entry<String, Student>> iterator = ek.iterator();//取出迭代器
while (iterator.hasNext()) {
    Map.Entry<String, Student> next =  iterator.next();
    System.out.println(next.getKey()+" - "+next.getValue());
}

[HashMap 是一個雙列集合,以 K-V] 的方式存儲對象,因此在使用泛型時要向其中傳入兩個類型

我們都知道使用迭代器遍歷 HashMap 時要先通過entrySet()取出鍵值對,然後通過轉型得到對應的類來得到對象信息。而在使用泛型定義[K-V]就規定了取出的鍵值對的類型,所以就省去了轉型這一步驟,同樣也使程序變得簡單,高效。

四、泛型使用細節

1.<> 中類型規範

2. 繼承性體現

在給泛型指定具體類型後,可以傳入該類型或者其子類類型

P<A> ap = new P<A>(new A());
P<A> ap1 = new P<A>(new B()); //A的子類
class A {}
class B extends A{}

3. 簡寫形式

 P<A> ap = new P(new A());

五、自定義泛型

1. 自定義方法使用類聲明的泛型

在形參列表中傳入的數據類型與泛型不一致時會報錯,體現規範性

public static void main(String[] args) {
    U<String, Double, Integer> u = new U<>();
    u.hi("hello", 1.0);  //X->String Y->Double
}
class U<X, Y, Z> {
    public void hi(X x, Y y) {} //使用類聲明的泛型
}

2. 自定義泛型方法

public static void main(String[] args) {
    U<String, Double, Integer> u = new U<>();
    u.m1("xx",22);
    //當調用方法時,傳入參數編譯器會自己確定類型 會自動裝箱
}
class U<X, Y, Z> {
    public <X,Y> void m1(X x,Y y){} //自定義泛型方法
}

這裏的自動裝箱很有意思,他是在三個類型中自動匹配當前輸入的數據類型,也不用考慮順序問題,如圖所示

3. 注意事項

①泛型數組不能初始化,因爲數組在 new 不能確定 A 的類型,就無法在內存開空間

錯誤寫法: A[] a=new A[];

②靜態方法不能使用類定義的泛型

原因是靜態成員是和類相關的, 在類加載時, 對象還沒有創建所以,如果靜態方法和靜態屬性使用了泛型,JVM 就無法完成初始化。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/DXzflYp1ZBavYTrI_4gbwg