2、泛型-泛型的约束与局限性

  • 不能用基本类型实例化类型参数

没有Pair<double>,只有Pair<Double>。原因是类型擦除,擦除之后,Pair类含有Object类型的域,而Object不能存储double值。这样做与java语言中基本类型的独立状态相一致。

  • 运行时类型查询只适用于原始类型
        Pair<LocalDate> pair=new DateInterval();
        System.out.println(pair instanceof Pair);//true
        
        Pair<String> p=new Pair<>();
        System.out.println(p instanceof Pair);//true

        Pair<String> stringPair = new Pair<>();
        Pair<LocalDate> localPair = new Pair<>();
        System.out.println(stringPair.getClass() == localPair.getClass());//true

试图查询一个对象是否属于某个泛型类型,只能测试是否是任意类型的Pair原始类型,if(pair instanceof Pair<LocalDate>)编译器会报错。 同理,getClass方法只是返回原始类型。所以不能用instanceof

  • 泛型类的静态上下文中类型变量失效
public class Singleton<T> {

    private static T singInstance; //编译器报错

    public static T getSingleInstance(){//编译器报错
        if(singInstance==null){
            singInstance=new T();//编译器报错
        }
        return singInstance;
    }
}

不能在静态域或方法中引用类型变量。因为泛型是要在对象创建的时候才知道是什么类型的,而对象创建的代码执行先后顺序是static的部分,然后才是构造函数等等。所以在对象初始化之前static的部分已经执行了,如果你在静态部分引用的泛型,那么毫无疑问虚拟机根本不知道是什么东西,因为这个时候类还没有初始化。

  • 不能抛出或捕获泛型类的实例

泛型类对象不能扩展Throwable。
public class Problem<Pair> extends Exception{...}报错。
public class Problem extends Exception{...} 正确。

catch中不能使用类型变量,因为try时抛出的异常不一定是T指定的异常。

public class Problem{
    
    public static <T extends Throwable> void doWork(T t){
        try{
            
        }catch (T e){//error
            
        }
    }
}

下面这种方法可以

public class Problem{

    public static <T extends Throwable> void doWork(T t) throws T{//此时可以throws T
        try{

        }catch (Throwable e){//捕获到具体实例
            t.initCause(e);
            throw t;//这时候抛具体实例,所以throw t  throws T 是可以的!
        }
    }
}
  • 不能创建参数化类型的数组

ArrayList<T>[] intArr = new ArrayList<T>[10];参数化类型数组。
T[] intArr = new T[10]; 泛型数组。

        ArrayList<Integer>[] intArr = new ArrayList<Integer>[10];//error
        Object[] obj = intArr;

        ArrayList<String> listStr = new ArrayList<>();
        obj[0] = listStr;
        //上面相当于使用数组移花接木,让编译器把ArrayList<Integer>当做了ArrayList<String>
        ArrayList<Integer> listInt = intArr[0];
        Integer data = listInt.get(0);//想要Integer,但却是String

假如允许泛型数组,第一行是正确的,那么类型擦除后,可以转换为Object[],然后向Object[]放ArrayList<String>,而不是我们要的ArrayList<Integer>。因为在运行时,类型是擦除的,运行时系统无法对数组中存储的类型做检查,系统只是看到像intArr数组里面放ArrayList对象。
Integer data = listInt.get(0)会被替换成Integer data =(Integer) listInt.get(0),但是data实际上指向ArrayList<String>,转为Integer,会报ClassCastException。
最后一行改为Object obj=listInt.get(0);它实际引用的是Integer类型,底层却是String类型,如果调用hashCode(),我们以为它执行的是Integer的hashCode,但它执行的是String的hashCode,那就很难发现错误。
如果能用泛型数组,就有一个矛盾的现象,泛型的出现就是为了消灭ClassCastException而出现,但是用泛型数组,它就可能引发ClassCastException,所以就不支持泛型数组。
声明类型为ArrayList<Integer>[]的变量是合法的,只是不能创建这些实例。

  • 不能直接创建泛型数组

ArrayList<T>[] intArr = new ArrayList<T>[10];参数化类型数组。
T[] intArr = new T[10]; 泛型数组。 不能使用new T[10]来创建泛型数组。解决方案可以使用泛型数组包装器,比如ArrayList。

    public static <T extends Comparable> T[] minmax(T... a){
        T[] ts = (T[]) Array.newInstance(a.getClass().getComponentType(), 2);//创建长度为2的数组
        ts[0]=a[0];
        ts[1]=a[1];
        ts[2]=a[2];****
        return ts;
    }
    
    //调用
    String[] minmax = minmax("a", "b", "c");

public Class<?> getComponentType()返回表示数组组件类型的Class,如果此类不表示数组,则此方法返回null。

  • 不能实例化类型变量

不能使用像new T(),new T[],T.class这样的表达式中的类型变量。类型擦除后变成Object,本意肯定不希望调用new Object()。
要灵活创建泛型对象,最好的解决办法是提供一个构造器表达式。

    public static <T> Pair<T> makePair(Class<T> cl){
        Pair<T> tPair = null;
        try {
            tPair = new Pair<>(cl.newInstance(), cl.newInstance());
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return tPair;
    }
    //这个方法可以按照下列方式调用:
    Pair<String> p = Pair.makePair(String.class); 

不能使用T.class,这样是不合法的。Class类本身是泛型,比如String.class是一个Class<String>的实例,因此,makePair方法能够推断出pair类型。

  • 注意擦除后的冲突
public class Pair<T> {

    public boolean equals(T value) {//error
        return first.equals(value)&& second.equals(value);
    }
}    

Pair的equals方法报错。因为擦除后T变成Object,Pair的equals方法和Object的equals方法冲突,补救的方法就是Pair的equals改成其他名称。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值