java泛型常见问题

在《Java泛型总结》中已经详细介绍了泛型的相关知识,本文继续来看几个问题。

java泛型的通过对变量进行类型限制,使得在编译阶段尽可能发现更多的错误,因此强化了类型安全,同时消除了许多强制类型转换。为了与JDK5.0之前的版本保持兼容,编译器会把泛型代码首先转换为不带泛型的代码,具体分为以下几步:

1.将参数化类型中的类型参数"擦除"(erasure)掉;

2.将类型变量用"上限(upper bound)"取代,通常情况下这些上限是 Object。这里的类型变量是指实例域,本地方法域,方法参数以及方法返回值中用来标记类型信息的"变量",例如:实例域中的变量声明 A elem;方法声明 Node (A elem){};,其中,A 用来标记 elem 的类型,它就是类型变量。

3.添加类型转换并插入"桥方法"(bridge method),以便覆盖(overridden)可以正常的工作。

下面结合一些具体的问题来分析。

public class Collections {  
  public static  void copy(List dest, List src) {  
      for (int i=0; i

Java Collections中的拷贝函数如上所示,首先这里T作为类型变量,其中两个比较重要的用法是? extends T和? super T。他们之间是有非常大的区别的。? extends T说明某个类型继承自T,但是具体是什么类型不清楚,就好比化学实验室中有很多器皿,对于某个器皿我们只知道它是用来盛液体的,但是至于是那种液体无从而知,我们能把硫酸倒入进去吗?不能!说不定就会造成试剂污染。同样比如List我们只知道list中存放的是Number类型的对象,但是却无法向里面添加元素,只能从中取数。 另一个方面,我们知道集合不同于数组,List不是List的父类,所以不能进行赋值操作,? extends T的出现弥补了这一点。比如

List listI = new ArrayList();

List list = listI;

如果说? extends T指定了元素的上界,? super T 则指出了元素的下界。与? extends T不同的是它只能存数据不能取数据(这样说不是特别严格),因为我们只知道它是T的父类,但是同样无法确切的知道它的类型。(? extends T与? super T就好像各自指定了一个区间[null,T]---[T,Object])。

对于泛型有一个存取原则:如果只取数不放数,就用? extends T;如果只放数不取数,则用? super T;如果即取数又放数则用类型参数T。

像是上面代码中的copy函数就是对这个原则最好的诠释。关于copy函数,或许会有这样的疑问,这里可不可以把参数List用List代替,因为数据源一定继承自T嘛。看起来是这样的。但是有一点可以肯定,上面代码中的copy函数使用范围一定更广。(我还没有想到反例)

再看一段代码:

private Map> a = 
	new HashMap>();
这段代码编译时会报错:不兼容的类型。上面说过泛型的主要目的是对元素类型进行限制,加强类型安全,减少强制转换。这里我们看一下这段代码,为什么会报错呢?假如编译器允许这段代码通过检查,那么,map中第二个元素就不仅可以存放ArrayList还可以存放其他实现了List但不是ArrayList的对象,这不符合我们的预期。类似的,List list = new ArrayList();会产生编译错误,不要与下面这种情况混淆:

List list= new ArrayList();
list.add(new Integer(1));

接着看另外一段代码:

public static  List group(E ... numbers) {
    return Arrays.asList(numbers);
}
这样使用List ints = group(1, 2, 3);没有问题,但是List ints = group(1, 2, 3);这样就出现问题了。这里面涉及到编译器对类型的推断,第二种情况中,编译器会根据参数类型推断E为整型,把List引用赋值给List是错误的,二者没有继承关系。这时,我们需要显示的告诉编译器类型信息,可以这样使用List ints = MyClass.group(1, 2, 3);

最后再看一个来自statckoverflow的提问:

import java.util.*;
public class Test {
    public  void createA(List> c) {
        A a = new A(c);//warning here
    }
    public  void createB(List> c) {
        B b = new B(c);//error here: The constructor B(List>) is undefined
    }
}

interface I{
}
class B implements I{
    public B(List> c){
    }
}

class A implements I{
    public A(List> c){

    }
}

qs:B class is generic and A is not, but I have no idea why it matters in that case.

ans:直接贴出回答

public B(List> c) {
}
The ? is the unknown type. It is not a wild card for any other type. The compiler tells the truth, there is no constructor for the type List> (T is not ?)

It doesn't really work for the other method too. You simply exchanged the compile error by a "unchecked conversion" warning. Because class A is parametized, you'd have to call it like that:

public void createA(List> c) {
    A a = new A(c);
}
And voilà, say hello to the same error.







你可能感兴趣的:(Java)