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
*/