Java泛型--擦除

Java泛型--目錄
java

概述

   咱們能夠聲明ArrayList.class,可是不能聲明ArrayList<Integer>.class,看下面的例子:
數組

public class EqualClassName {
    public static void main(String[] args) {
        //聲明不一樣類型的list
        ArrayList<String> list1 = new ArrayList<>();
        ArrayList<Integer> list2 = new ArrayList<>();

        //查看他們的類型
        System.out.println(list1.getClass() == list2.getClass());
    }
}

/*輸出結果:
true

*/

    不一樣的類型在行爲上確定有不一樣,ArrayList<String>不能存在Integer的數據,ArrayList<Integer>也不能存Integer之外的數據,所已ArrayList<String>和ArrayList<Integer>很容易被認爲是不一樣的類型.可是事實確實他們是相同的類型.app

    下面是一個補充:ide

class Glass {

}
class SunGlass<A> {

}
class MulGlass<C,T> {

}
class LostInfo{
    public static void main(String[] args) {
        Glass glass = new Glass();
        SunGlass<Integer> sunGlass = new SunGlass();
        MulGlass<Integer,String > mulGlass = new MulGlass();
        System.out.println(Arrays.toString(glass.getClass().getTypeParameters()));
        System.out.println(Arrays.toString(sunGlass.getClass().getTypeParameters()));
        System.out.println(Arrays.toString(mulGlass.getClass().getTypeParameters()));
    }
}
/*輸出結果:
[]
[A]
[C, T]
*/

    getTypeParameters()將返回一個TypeVariable的數組,表示有泛型聲明的的表示的類型參數,如上面看到的,只能用做參數佔位符的標識符,並不是有用的信息.函數

    在泛型代碼內部,沒法獲取任何有關泛型參數類型的信息.this

  總結:Java泛型時使用擦除來實現的,這意味着當你使用泛型時,任何具體的類型信息都被擦除了,你惟一知道的就是你正在使用一個對象,在運行時被擦除成"原聲"的類型.所以List<String>和List<String>在運行時事實上時相同的類型,這兩種形式都被擦除成他們原生的類型,即List.
spa

    注意:.net

  • 因爲擦除的緣由,泛型不能顯示的引用運行時類型的操做之中,例如轉型,instanceof操做,new表達式等.code

擦除的邊界

    因爲擦除的緣由,使得泛型的內部沒法獲取有關參數類型的任何信息,也就不能就行任何類型內部的行爲,爲了解決 這個問題Java添加了邊界的功能,使泛型類型參數擦除到它的第一邊界.對象

class HashF{
    public void sayHello(){
        System.out.println("hello, this is HashF");
    }
}

class HoldHashF <T extends HashF>{
    private T hello;
    public HoldHashF(T hello){
        this.hello = hello;
        this.hello.sayHello();
    }

    public static void main(String[] args) {
        HoldHashF<HashF> mulHashF = new HoldHashF<>(new HashF());
    }
}

/*輸出結果:
hello, this is HashF
*/

    在這個例子中,咱們可用使用T調用HashF的sayHello()方法,編譯器並無把泛型參數類型徹底擦除,而是保留了部分信息,這就是擦除的邊界.這裏重用了extends關鍵字,T爲HashF或者HashF的子類,使T具備了HashF的動做.

擦除補償

   因爲擦除的緣由,使泛型在代碼中丟失了執行某些操做的能力,例如new,instanceof等,爲了補償這些功能,咱們可使用類型標籤進行補償.

instanceof

  以前描述了泛型不能使用instanceof,由於類型擦除,可是可用動態的調用isInstance(),具體請看以下的例子:

public class Isinstance<T> {
    private Class<T> type;

    public Isinstance(Class<T> type){
        this.type = type;
    }

    private boolean equalType(Object arg){
        return type.isInstance(arg);
    }

    public static void main(String[] args) {
        Isinstance<Integer> isinstance = new Isinstance<>(Integer.class);

        System.out.println(isinstance.equalType(new Integer(1)));
        System.out.println(isinstance.equalType(new String("11")));
    }
}
/*輸出結果:
true
false
*/

new

    泛型中不容許使用new T()這樣建立新的對象,可是咱們可用使用其餘方法建立,第一種時使用newInstance(),這種方法必須保證有默認的構造函數;另外一種是構造一個工廠對象,由工廠生成對象.

  • newInstance

class NewInstance {
    public <T> T createInstance(Class<T> type){
        try {
            T obj =  type.newInstance();
            System.out.println("create sucessfully. type:" + type.getName());
            return obj;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        System.out.println("create failed. type:" + type.getName());
        return null;
    }

    public static void main(String[] args) {
        NewInstance newInstance = new NewInstance();
        newInstance.createInstance(String.class);
        newInstance.createInstance(Integer.class);
    }
}
/*輸出結果:
create sucessfully. type:java.lang.String
create failed. type:java.lang.Integer
java.lang.InstantiationException: java.lang.Integer
	at java.lang.Class.newInstance0(Class.java:359)
	at java.lang.Class.newInstance(Class.java:327)
	at zhk.com.NewInstance.createInstance(newInstance.java:9)
	at zhk.com.NewInstance.main(newInstance.java:24)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:601)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
*/

    由上面的例子中能夠看出來,java.lang.Strin建立成功了,可是java.lang.Integerque建立失敗,拋出了java.lang.InstantiationException異常,這是覺得Integer的構造函數時必須有一個整型參數,這就是newInstance()的侷限性,只能建立有無參構造函數的類的對象.

  • 工廠對象

    使用工廠對象的方法具體怎麼建立一個類的對象是有用戶本身控制的,沒有newInstance()的條件限制,用戶可用根據本身的需求編寫不一樣類型的工廠.

//接口
interface IFatory<T> {
    T newInstance();
}

//Integer工廠
class IntegerFactory implements IFatory<Integer> {

    @Override
    public Integer newInstance() {
        return new Integer(0);
    }
}
//String工廠
class StringFactory implements IFatory<String> {

    @Override
    public String newInstance() {
        return String.valueOf("hello word");
    }
}

//總工廠
class Factory {

    Map<Class, IFatory> factoryMap = new HashMap<>();

    public void addFactory(Class type, IFatory fatory) {
        factoryMap.put(type, fatory);
    }

    public <T> T createInstance(Class<T> type) {
        IFatory<T> factory = factoryMap.get(type);
        T item = factory.newInstance();
        return item;
    }

    public static void main(String[] args) {
        Factory factory = new Factory();
        factory.addFactory(Integer.class, new IntegerFactory());
        factory.addFactory(String.class, new StringFactory());

        Integer intValue = factory.createInstance(Integer.class);
        System.out.println(intValue.toString() );
        String stringValue = factory.createInstance(String.class);
        System.out.println(stringValue.toString());
    }
}

/*輸出結果:
0
hello word
*/
相關文章
相關標籤/搜索