泛型—— 一種可以接收數據類型的數據類型,本文將通俗講解Java泛型的優點、方法及相關細節。 一、泛型的引入 我們都知道,繼承是面向對象的三大特性之一,比如在我們向集合中添加元素的過程中add()方法里填入的是Object類,而Object又是所有類的父類,這就產生了一個問題——添加的類型無法做到統 ...
泛型—— 一種可以接收數據類型的數據類型,本文將通俗講解Java泛型的優點、方法及相關細節。
一、泛型的引入
我們都知道,繼承是面向對象的三大特性之一,比如在我們向集合中添加元素的過程中add()方法里填入的是Object類,而Object又是所有類的父類,這就產生了一個問題——添加的類型無法做到統一 由此就可能產生在遍歷集合取出元素時類型不統一而報錯問題。
例如:我向一個ArrayList集合中添加Person類的對象,但是不小心手賤添加了一個Boy類的對象,這就會導致如下結果
傳統的方式不能對加入到集合ArrayList中的數據類型進行約束(不安全)遍歷的時候,需要進行類型轉換,如果集合中的數據量較大,對效率有影響 這就極大地降低了程式的健壯性,因此設計者針對此問題引入了泛型!
更多集合系列教程:https://www.javastack.cn/categories/Java/
二、使用泛型的好處
1.提升了程式的健壯性和規範性
針對上述問題,當我們採用泛型就會顯得非常簡單,只需要在編譯類型後利用泛型指定一個特定類型,編譯器就會自動檢測出不符合規範的類並拋出錯誤提示
2.編譯時,檢查添加元素的類型,提高了安全性
3.減少了類型轉換的次數,提高效率
- 當不使用泛型時:
- 當使用泛型時:
4.在類聲明時通過一個標識可以表示屬性類型、方法的返回值類型、參數類型
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”相當於這裡的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的是我事先定義好的一個類,把它放到其中作為泛型來約束傳入的對象,以及在遍歷時減少轉型的次數提高效率
2.使用泛型方式給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就無法完成初始化。
來源:iyu77.blog.csdn.net/article/details/125304857
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2022最新版)
4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!
覺得不錯,別忘了隨手點贊+轉發哦!