Java核心技術第八章——泛型程式設計(1)
1.泛型程式設計
泛型程式設計意味著編寫的程式碼可以被很多不同型別的物件所重用。例如:不希望為了聚集String和Integer物件分別設計不同的類。(個人覺得此處說的聚集譯為:建立一個物件,屬性可以為String和Integer型別。但是卻有著相同的行為或屬性)
程式碼如下:
public class StringTest { private String age; public String getAge() { return age; } public void setAge(String age) { this.age = age; } }
public class IntegerTest { private Integer age; public Integer getAge() { return Age; } public void setAge(Integer age) { this.age= age; } }
可以看到上面兩個類都有size屬性和方法。只是他們的屬性型別有所不同。那麼我們則可以使用到泛型:例
public class GenericTest{ private T size; public T getSize() { return size; } public void setSize(T size) { this.size = size; } }
然後只需要如下呼叫
public static void main(String[] args){ GenericTestgenericString= new GenericTest(); GenericTest genericInteger = new GenericTest(); }
這樣我們就很好的解決了程式碼重用的問題,不是嗎?看不懂?不急,我只是說了泛型的好處,讓我們慢慢往下了解。
2.定義簡單的泛型類
沒接觸過泛型的同學看到上面例子中的T可能會覺得一臉懵逼,其實T只是一個類的型別變數(雖然你可以定義A、B、C...,但是我們還是要規範點的是吧),然後T需要使用尖括號括起來,當你定義多個型別變數時。可以看下面的Generic類,
public class Generic{ private T size; private U length; ... }
當你傳入Generic
public class Generic { private Integer size; private String length; ... }
所以,泛型類其實可以看做普通的工廠類。
3.泛型方法
上面介紹了一個簡單的泛型類,讓我們來看看泛型方法是怎麼寫吧。
public class ArrayAlg { public staticT getMiddle(T... a){ return a[a.length/2]; } }
可以看到,ArrayAlg是一個普通類,並不是一個泛型類。說明:泛型方法可以定義在普通類和泛型類中。
當我們呼叫getMiddle方法時,在方法名前的尖括號放入具體型別:
String s = ArrayAlg.getMiddle("John","Q","Min");
看起來是不是很彆扭?其實可以省略
String s = ArrayAlg.getMiddle("John","Q","Min");
有的小夥伴可能會想到,醬紫的話,我能不能瞎比的傳引數進去?例:
Double middle2 = ArrayAlg.getMiddle(3.14,0);
其實當你這樣寫的時候編譯器就已經提示錯誤了,因為第二個引數編譯器會預設認為是Integer型別。解決方法有兩種:
1.把Double改成Number型別,因為Number型別為Double和Integer的父類。
2.自己改程式碼去吧,誰讓你瞎搞。
4.型別變數的限定
有時候,類或方法需要對型別變數加以約束,假如,某個泛型方法需要使用到Comparable介面的compareTo方法,那我們則需要限制傳進來的引數必須實現Comparable介面,例:
public staticT minmax(T[] a){ compareTo... }
傳進來的引數a則必須實現Comparable介面。假如傳進來引數需要實現Comparable和Serializable兩個介面,則
public staticPair minmax(T[] a){ compareTo... }
叮咚!那麼傳進來的引數需要繼承某個類怎麼寫?其實是一樣的。傳進來的引數需要繼承某個類、實現某個介面都是使用extends關鍵字來控制。原因:選擇關鍵字extends的原因是更接近子類的概念,並且Java的設計者也不打算在語言中再新增一個新的關鍵字
5.泛型程式碼和虛擬機器
5.1型別擦除
Java 的泛型在編譯器有效,在執行期被刪除,也就是說所有泛型引數型別在編譯後都會被清除掉
例如:
public class GenericClass{ private T first; public T getFirst() { return first; } ... }
擦除型別後
public class GenericClass { private Object first; public Object getFirst() { return first; } ... }
可以看到,型別擦除後的GengericClass和沒引用泛型沒有什麼兩樣。而且,當你使用GenericClass
但是當我們使用限定型別變數後,型別擦除後將會使用限定型別。例:
public class GenericClass{ private T first; ... }
可以看到限定了傳進來的引數需要實現Comparable介面,那麼型別擦除後如下:
public class GenericClass{ private Comparable first; ... }
問題:當你限定了引數需要實現Comparable和Serializable介面,就是GenericClass
public class GenericClass{ private Comparable first; ... }
虛擬機器會把首位實現的介面(此處為Comparable),轉為擦除後物件型別。當然,如果你這樣寫GenericClass
5.2翻譯泛型表示式
當程式呼叫泛型方法時,如果擦除返回型別, 編譯器將插入強制型別轉換。例如List集合原始碼:
public interface Listextends Collection { ... E get(int index); ... }
當不使用型別變數時,取出來的值是Object,而定義了String型別變數時,編譯器將插入強制型別轉換:
List list1 = new ArrayList(); list1.add("1"); list1.get(0); //ObjectListlist2 = new ArrayList(); list2.add("1"); list2.get(0); //String
但是在虛擬機器中時,list2.get(0)在呼叫時翻譯成了兩條虛擬機器命令:
1.對原始方法List集合的get方法呼叫
2.將返回的Object進行強制型別轉換成String
5.3翻譯泛型方法
當GenericSubClass類繼承了Generic類時,泛型方法會有什麼變化?
public class Generic{ private T size; public T getSize() { return size; } ... }
public class GenericSubclass extends Generic{ @Override public Integer getSize() { return super.getSize(); } }
此時的@Override真的是重寫父類方法嗎?到了執行期間,Generic被型別擦除後getSize方法返回型別變成Object:
public Object getSize() { return size; }
而GenericSubclass的getSize方法的返回型別卻是Integer:
public Integer getSize() { return super.getSize(); }
這並不是重寫父類的方法,因為方法並不一樣,所以導致GenericSubclass在虛擬機器中卻有了兩個getSize方法:
public Integer getSize() { ... }public Object getSize() { ... }
導致的原因就是型別擦除與多型發生了衝突。要解決此問題,虛擬機器自動在GenericSubclass中生成了一個橋方法:
public Integer getSize() { return (Integer)super.getSize(); }
最後,虛擬機器就會呼叫自己生成的橋方法來解決此衝突。
原文出處:https://www.cnblogs.com/Johnson-lin/p/9580971.html
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2459/viewspace-2814023/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 《Java核心技術(卷1)》筆記:第8章 泛型程式設計Java筆記泛型程式設計
- java 泛型程式設計Java泛型程式設計
- 泛型程式設計泛型程式設計
- 02. 程式設計核心內功心法之泛型程式設計泛型
- Java核心技術總結一:Java的基本程式設計結構Java程式設計
- Java核心知識1:泛型機制詳解Java泛型
- java筆記-two-java泛型程式設計(簡記)Java筆記泛型程式設計
- 十、GO程式設計模式 : 泛型程式設計Go程式設計設計模式泛型
- Java核心之細說泛型Java泛型
- 泛型程式設計詳解(一)泛型程式設計
- 泛型程式設計與 OI——modint泛型程式設計
- 好程式設計師Java教程分享Java難點解析之泛型程式設計師Java泛型
- 物件導向程式設計和`GP`泛型程式設計物件程式設計泛型
- 好程式設計師Java培訓Java程式設計師必學技術程式設計師Java
- 【java】【泛型】泛型geneticJava泛型
- Java核心技術卷閱讀隨筆--第3章【Java 的基本程式設計結構】Java程式設計
- Java外包程式設計師的技術出路Java程式設計師
- GO語言泛型程式設計實踐Go泛型程式設計
- Java程式設計師必讀:最新流行的Java開發程式設計技術Java程式設計師
- 好程式設計師Java學習路線分享JavaEE的13種核心技術程式設計師Java
- 泛型最佳實踐:Go泛型設計者教你如何用泛型泛型Go
- Go 泛型的這 3 個核心設計,你都知道嗎?Go泛型
- 【Java 核心技術 1】時間 Date、LocalDate 練習JavaLDA
- [Java 核心技術 1] 時間 Date、LocalDate 練習JavaLDA
- 2020年Java程式設計師需要哪些技術Java程式設計師
- Swift使用協議加泛型程式設計(一)Swift協議泛型程式設計
- Rust 程式設計影片教程(進階)——001 泛型Rust程式設計泛型
- 在C語言中實現泛型程式設計C語言泛型程式設計
- C語言如何實現泛型程式設計?C語言泛型程式設計
- Java泛型Java泛型
- Java中的泛型程式設計:深入理解型別引數與型別邊界的使用Java泛型程式設計型別
- java核心技術卷1 第五章:繼承Java繼承
- java核心技術卷1學習思維導圖Java
- 好程式設計師Java培訓分享Java之反射技術程式設計師Java反射
- Java技術分享之函數語言程式設計!Java函數程式設計
- Java技術分享之函數語言程式設計Java函數程式設計
- Rust 程式設計視訊教程(進階)——001 泛型Rust程式設計泛型
- 使用 Go 泛型的函數語言程式設計Go泛型函數程式設計