Java泛型的约束和局限性

在使用Java泛型时需要考虑一些限制, 大多数限制都是由于类型擦除所引起的。

1.不能使用基本类型实例化类型参数

类型参数 T不能取8中基本类型,需要的时候采用包装器类型,如果不能接受这样的替换时, 可以使用独立的类和方法来处理。

2.运行时的类型查询只会产生原始类型(以instanceof和getClass为例)

    if(a instanceof Pair<String>)                  //error
    if(a instanceof Pair<?>)
    if(a instanceof Pair)                          //只能检查是否是一个Pair

    Pair<String> stringPair = ....;
    Pair<Employee> empPair = ...;
    if(stringPair.getClass() == empPair.getClass())//equal,返回Pair.class

3.不能创建参数化类型的数组

不能创建泛型数组,想要收集泛型对象, 只有一种安全有效的方法, 例如:

    pair<String>[] table = new Pair<String>[10];   //error
    List list = new ArrayList<Pair<String>>();

    String[] table = new String[10];               //普通数组
    Object[] oArray = table;                       //赋给父类       
    table[1] = 1;                                  //编译不通过,不能转换

    Pair<String>[] table = new Pair<String>[10];   //不可以,假设可以
    //擦除以后, table的类型是Pair[]。可以将其转换成Object[]                                                                                       
    Object[] oArray = table;                      
    table[0] = new Pair<Employee>();               //由于擦除可以通过数组存储检查,但仍然会导致类型错误, 所以不允许
note:只是不允许创建泛型数组, 而声明变量是合法的,只不过不能初始化。想要初始化可以采用下面的形式(数组的类型不可以是类型变量,而采用通配符的方式), 不过结果不安全, 因为随时可能产生类型错误。
    Pair<String>[] table = (Pair<String>[]) new Pair<?>[10];//合法

4.Varargs警告

    public static <T> void addAll(Collection<T> coll, T...ts) {
        for(T t : ts) coll.add(t);
    }

    Collection<Pair<String>> table = new HashSet<>();
    Pair<String> pair1 = new Pair<>();
    Pair<String> pair2 = new Pair<>();
    addAll(table, pair1, pair2);
    //code3
    @SafeVarargs
    public static <T> void addAll(Collection<T> coll, T... ts)

这里的参数ts事实上是一个装有提供参数的数组,这里放宽了规则。关于变长泛型参数的传入,有警告不过不会报错。可以对仅仅读取泛型数组元素的方法使用code3中的Annotation。

5.不能实例化类型变量

    public Pair() { first = new T(); second = new T(); }   // Error

因为类型擦除会把T改变成Object, 显而易见new Object()没有什么意义,所以不能实例化类型变量。在Java8中采用构造器表达式来解决这个问题,如下例所示:

    Pair<String> p = Pair.makePair(String::new);
    public static <T> Pair<T> makePair(Supplier<T> constr) {
        return new Pair<>(constr.get(), constr.get());
}

6.不能构造一个泛型数组

    public static <T extends Comparable> T[] minmax(T[] a) { T[] mm = new T[2]; . . . } // Error

类型擦除将总会使得这个方法构造一个数组Comparable[2].
事实上,如果想使用的这样一个数组只是被当做一个类的私有域来使用, 可以通过将这样的数组声明为Object[], 并在查询元素时使用强制类型转换。例如ArrayList<E>可以这样实现:

    public class ArrayList<E> {
        private Object[] elements;
        . . .
        @SuppressWarnings("unchecked") public E get(int n) { return (E) elements[n]; }
        public void set(int n, E e) { elements[n] = e; } // no cast needed
        }

7.类型变量不能出现在泛型类的静态上下文中

    public class Singleton<T> {
        private static T singleInstance;          // Error
        public static T getSingleInstance() {     // Error
            if (singleInstance == null) 
                construct new instance of T
        return singleInstance;
    }
}

泛型类上的泛型类型在实例化时确定。 比如:

Singleton<String> singletonForString= new Singleton(); 
Singleton<Boolean> singletonForBoolean= new Singleton(); 

但静态成员是被该类的所有对象共享的,所以泛型类型不能应用到静态成员上。

8.不能抛出或捕获泛型类实例

Java里面既不能抛出也不能捕获泛型类异常。
1.泛型类不能extends Throwable。

    public class Problem<T> extends Exception { /* . . . */ } // Error

2.不可以在catch中使用类型变量,例如:

    public static <T extends Throwable> void doWork(Class<T> t) {
        try {
            System.out.println();
        } catch (T e) {// Error--can't catch type variable
            e.printStackTrace();
        }
    }

3.在异常规范中是可以中使用泛型的,例如:

    public static <T extends Throwable> void doWork(T t) throws T {
    try {
        ...;
    } catch (Throwable realCause) {
        t.initCause(realCause);
        throw t;
    }
}

9.注意类型擦除后的冲突

要注意到在类型擦除后的冲突,例如:

    public class Pair<T> {
        public boolean equals(T value) { 
            return first.equals(value) && second.equals(value); 
        }
        . . .
}

对于Pair<String>,就存在两个方法:

    boolean equals(String) // defined in Pair<T>
    boolean equals(Object) // inherited from Object

擦除后的boolean equals(T) 就是与Object.equals 冲突的 boolean equals(Object)

这里写图片描述`

泛型规范的另一条规则是:
To support translation by erasure, we impose the restriction that a class or type variable may not at the same time be a subtype of two interface types which are different parameterizations of the same interface.例如:

    class Employee implements Comparable<Employee> { . . . }
    class Manager extends Employee implements Comparable<Manager> { . . .} // Error

Manager 对象这里就是不合法的。原因可能是在桥方法上存在冲突。一个类实现了Comparable接口就会有一下桥方法:

    public int compareTo(Object other) { return compareTo((X) other); }

这里就会出现两个桥方法,可能出现冲突?待证!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值