不能用基本型別實體化型別引數
不能用型別引數代替基本型別:例如,沒有Pair,只有Pair,其原因是型別擦除,擦除之后,Pair類含有Object型別的域,而Object不能存盤double值,這體現了Java語言中基本型別的獨立狀態,
運行時型別查詢只適用于原始型別(raw type)
運行時:通常指在Classloader裝載之后,JVM執行之時
型別查詢:instanceof、getClass、強制型別轉換
原始型別:即(raw type),泛型型別經編譯器型別擦除后是Object或泛型引數的限定型別(例如Pair,Comparable就是T的限定型別,轉化后泛型的原始型別就是Comparable,所以Pair類不帶泛型是Pair),即Pair類含有Comparable型別的域
JVM中沒有泛型
if(a instanceof Pair<String>) //ERROR,僅測驗了a是否是任意型別的一個Pair,會看到編譯器ERROR警告
if(a instanceof Pair<T>) //ERROR
Pair<String> p = (Pair<String>) a;//WARNING,僅測驗a是否是一個Pair
Pair<String> stringPair = ...;
Pair<Employee> employeePair = ...;
if(stringPair.getClass() == employeePair.getClass())
//會得到true,因為兩次呼叫getClass都將回傳Pair.class
//加入Java開發交流君樣:756584822一起吹水聊天
不能創建引數化型別的陣列(泛型陣列)
引數化型別的陣列:指型別帶有泛型引數的陣列,也即泛型陣列,如Pair[] 、 T[]
不能實體化引數化型別的陣列,例如:
Pair<String> table = new Pair<String>[10]; //ERROR
在這里我們假設可以實體化,那么經編譯器型別擦除后,table的型別是Pair[],我們再讓它協變為Object[]:
Object[] objArray = table;
而一般來說,陣列會記住他的元素型別Pair,我們如果試圖存盤其他型別的元素,就會拋出例外(陣列存盤檢查),例如:
objArray[0] = "Hello"; //ERROR--component type is Pair
但是,對于泛型型別Pair,型別擦除會使這種不同類檢查機制無效,這就是不能實體化泛型陣列的原因!
objArray[0] = new Pair<Employee>();
//如果泛型機制允許我們實體化陣列,那么這一步就沒理由出錯了!
//而這違背了我們的初衷(限定型別)
陣列存盤只會檢查擦除后的型別,又因為Java語言設計陣列可以協變,所以可以通過編譯
能夠通過陣列存盤檢查,不過仍會導致一個型別錯誤,故不允許創建引數化型別的陣列
注意,宣告型別為Pair[]的變數是合法的,只是不能創建這些實體(我們應該直接用new Pair[10]{…}來初始化這個變數)
泛型陣列的間接實作:
通過泛型陣列包裝器,如ArrayList類,維護一個Object陣列,然后通過進出口方法set、get來限定型別和強制轉換陣列型別,從而間接實作泛型陣列,
例如:ArrayList: ArrayList<Pair<T>>、ArrayList<T>
不能實體化型別變數T
即不能使用new T(..) , new T[..] 或 T.class這樣的運算式中的型別變數
例如: public Pair() { first = new T(); } //ERROR!型別擦除將T改變成Object,呼叫非本意的new Object()
不能使用new T(..)
但是,可通過反射呼叫Class.newInstance方法來構造泛型物件(要注意運算式T.class是非法的)
public static <T> Pair<T> makePair(Class<T> cl){
try{ return new Pair<>(cl.newInstance() , cl.newInstance()); }
catch(Exception ex) { return null; }
}
//加入Java開發交流君樣:756584822一起吹水聊天
//這個方法可以按照下列方式呼叫:
Pair<String> p = Pair.makePair(String.class);
注意:Class類本身是泛型,String.class是一個Class的實體,因此makePair方法能夠推斷出pair的型別
不能使用new T[…]
解決方案:使用泛型陣列包裝器,例如ArrayList
然而,當在設計一個泛型陣列包裝器時,例如方法minmax回傳一個T[]陣列,則泛型陣列包裝器無法施展,因為型別擦除,return (T [])new Object是沒有意義的強轉不了,此時只好利用反射,呼叫Array.newInstance:
import java.lang.reflect.*;
...
public static <T extends Comparable> T[] minmax(T... a){
T[] mm = (T[]) Array.newInstance(a.getClass().getComponentType() , 2);
...
}
【API檔案描述】public Class<?> getComponentType() 回傳表示陣列組件型別的 Class,如果此類不表示陣列類,則此方法回傳 null,
而ArrayList類中的toArray方法的實作就麻煩了
public Object[] toArray() 無參,回傳Object[]陣列即可
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
【API檔案描述】public static T[] copyOf(T[] original,int newLength)
復制指定的陣列,截取或用 null 填充(如有必要),以使副本具有指定的長度,對于在原陣列和副本中都有效的所有索引,這兩個陣列將包含相同的值,對于在副本中有效而在原陣列無效的所有索引,副本將包含 null,當且僅當指定長度大于原陣列的長度時,這些索引存在,所得陣列和原陣列屬于完全相同的類,
public T[] toArray(T[] a) a - 要存盤串列元素的T[]陣列(如果它足夠大)否則分配一個具有相同運行時型別的新陣列,回傳該T[]陣列
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass()); //a.getClass()得運行時目的陣列的運行時型別//加入Java開發交流君樣:756584822一起吹水聊天
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
【API檔案描述】
public static <T,U> T[] copyOf(U[] original,int newLength, Class<? extends T[]> newType)
復制指定的陣列,截取或用 null 填充(如有必要),以使副本具有指定的長度,對于在原陣列和副本中都有效的所有索引,這兩個陣列將包含相同的值,對于在副本中有效而在原陣列無效的所有索引,副本將包含 null,當且僅當指定長度大于原陣列的長度時,這些索引存在,所得陣列屬于 newType 類,
泛型類的靜態背景關系中型別變數無效
泛型類不能在靜態域或靜態方法中參考型別變數
public class Singleton<T>{
private static T singleInstance; //ERROR
public static T getSingleInstance(){...} //ERROR
}
型別擦除后只剩下Singleton類,因為靜態所以他只包含一個singleInstance域,如果能運行則以Singleton類為模板生成不同型別的域,因此產生了沖突
不能throws或catch泛型類的實體(有關例外)
泛型類繼承Throwable類不合法,如public class Problem<T> extends Exception {...}//ERROR 不能通過編譯
catch子句不能使用型別變數
public static <T extends Throwable> void doWork(Class<T> t){
try{
do work
}catch (T e){ // ERROR
Logger.global.info(...)
}
}
不過,在例外規范中使用型別變數是允許的:
public static <T extends Throwable> void doWork(T t) throws T { //此時可以throws T
try{//加入Java開發交流君樣:756584822一起吹水聊天
do work
}catch (Throwable realCause){ //捕獲到具體實體
t.initCause(realCause);
throw t; //這時候拋具體實體,所以throw t 和 throws T 是可以的!
}
}
此特性作用:可以利用泛型類、型別擦除、SuppressWarnings標注,來消除對已檢查(checked)例外的檢查,
unchecked和checked例外: Java語言規范將派生于Error類或RuntimeException的所有例外稱為未檢查(unchecked)例外,其他的是已檢查(checked)例外
- Java例外處理原則:必須為所有已檢查(checked)例外提供一個處理器,即一對一個,多對多個
@SuppressWarnings("unchecked")
//SuppressWarning標注很關鍵,使得編譯器認為T是unchecked例外從而不強迫為每一個例外提供處理器
public static <T extends Throwable> void throwAs(Throwable e) throws
T{ //因為泛型和型別擦除,可以傳遞任意checked例外,例如RuntimeException類例外
throw (T) e;
}
假設該方法放在類Block中,如果呼叫 Block.throwAs(t); 編譯器就會認為t是一個未檢查的例外
public abstract class Block{
public abstract void body() throws Exception;
public Thread toThread(){
return new Thread(){
public void run(){
try{
body();
}catch(Throwable t){
Block.<RuntimeException>throwAs(t);
}//加入Java開發交流君樣:756584822一起吹水聊天
}
};
}
@SuppressWarnings("unchecked")
public static <T extends Throwable> void throwAs(Throwable e) throws T{
throw (T) e ;
}
}
再寫個測驗類
public class Test{
public static void main(String[] args){
new Block(){
public void body() throws Exception{
//不存在ixenos檔案將產生IOException,checked例外!
Scanner in = new Scanner(new File("ixenos"));
while(in.hasNext())
System.out.println(in.next());
}//加入Java開發交流君樣:756584822一起吹水聊天
}.toThread().start();
}
}
- 啟動執行緒后,throwAs方法將捕獲執行緒run方法所有checked例外,“處理”成unchecked
Exception(其實只是騙了編譯器)后拋出;
有什么意義?正常情況下,因為run()方法宣告為不拋出任何checked例外,所以必須捕獲所有checked例外并“包裝”到未檢查的例外中;意義:而我們這樣處理后,就不必去捕獲所有并包裝到unchecked例外中,我們只是拋出例外并“哄騙”了編譯器而已
注意擦除后的沖突
Java泛型規范有個原則:“要想支持擦除的轉換,就需要強行限制一個泛型類或型別變數T不能同時成為兩個介面型別的子類,而這兩個介面是統一介面的不同引數化”
注意:非泛型類可以同時實作同一介面,畢竟沒有泛型,很好處理
class Calender implements Comparable<Calender>{...}
class GGCalender extends Calender implements Comparable<GGCalender>{...} //ERROR
在這里GGCalender類會同時實作Comparable 和 Comparable,這是同一介面的不同引數化

最新2020整理收集的一些高頻面試題(都整理成檔案),有很多干貨,包含mysql,netty,spring,執行緒,spring cloud、jvm、原始碼、演算法等詳細講解,也有詳細的學習規劃圖,面試題整理等,需要獲取這些內容的朋友請加Q君樣:756584822
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/241309.html
標籤:java
上一篇:java基礎演算法題(末考)總結三(冒泡、楊輝、回文等)
下一篇:Java入門
