Java:Raw Types vs. Generics

时间:2017-03-20 02:36:57

标签: java arrays generics compiler-warnings

考虑下面的示例代码

/* The new Java 5 or later Generic code */
class TestInserter {
    public static void main(String[] ar) {
        List<Integer> myList = new ArrayList<Integer>();
        myList.add(20);
        myList.add(42);
        Inserter ins = new Inserter();
        ins.insert(myList);
    }
}

/* Legacy Code */
class Inserter {
    public void insert(List list) {
        list.add(new Integer(55));
    }
}

编译并执行上面的代码将运行得很好,没有编译器或JVM的任何抱怨。 non type-safe insert()方法在我们的type-safe ArrayList中插入一个新的Integer对象。但是,如果我将insert方法更改为以下内容:

public void insert(List list) {
        list.add(new String("55"));
}

说什么?!上面的代码会运行吗?考虑一下,确定它会运行,奇怪但是上面的代码编译并运行得很好。 这与Arrays略有不同,它为您提供编译时和运行时保护,并防止此类事件发生。他们为什么这样做泛型?为什么Java允许泛型放入指定类型以外的值?!

2 个答案:

答案 0 :(得分:1)

现在回答。使用insert方法将Integer添加到列表中是非常安全和可允许的,因为它与我们为myList变量指定的类型相匹配。但是,当我们尝试将String插入到仅包含ArrayList值的Integer时,则会出现问题,不是在编译时,而是在运行时,当您尝试在错误添加的Integer实例上调用String特定方法时。

为了理解整个问题及其目的,您应该理解一件事 - JVM不知道您试图将String插入ArrayList意味着只保留Integer s。所有的泛型及其类型安全性仅限编译时间。通过名为&#34; 类型擦除&#34;的过程,编译器从通用代码中删除所有类型参数。换句话说,即使你写了这样的东西:

List<Integer> myList = new ArrayList<>();

在编译器完成之后成为以下代码:

List myList = new ArrayList();

然而,他们为什么要像这样保留泛型?

答案很简单!如果它不会出现这种奇怪的行为,那么早期Java版本的遗留代码就会被破坏,而且Million Java开发人员必须编辑Trilions的旧Java代码才能使其再次运行!!

但不要责怪编译器;当您尝试运行代码时,编译器会尝试使用以下警告警告您:

$> javac TestInserter.java  
Note: TestInserter.java uses unchecked or unsafe operations.  
Note: Recompile with -Xlint:unchecked for details.

当你按照编译器要求的方式执行操作时如下:

$> javac -Xlint:unchecked TestInserter.java
TestInserter.java:15: warning: [unchecked] unchecked call to add(E) as a member of the raw type List  
        list.add(new String("55"));  
                 ^  
where E is a type-variable:  
  E extends Object declared in interface List  
1 warning    

就编译器而言,它试图告诉您它怀疑程序中的某些代码可能最终遇到麻烦。

要将其包装起来,将泛型视为编译时保护。编译器使用类型信息(参数中指定的类型)来确保您不会将错误的内容插入集合(或用户定义的泛型类型),并且您不会从错误中获取值参考类型。 所有通用保护都是编译时间!这就是它。

答案 1 :(得分:0)

编译时泛型几乎涵盖了需要泛型类型断言的所有情况。对于需要在运行时泛型类型安全性中进行设计的极少数情况,您可以存储运行时类型标记(RTTT),Class<T>的实例,其中{{1}是通用参数。

T

除了你大部分时间都不需要运行时泛型之外,Java在引入泛型时遇到了问题。使它们的运行时可执行(“可恢复”)将破坏一堆遗留代码或使语言变得非常复杂。默认情况下,使用泛型编译时,允许遗留代码以原始类型的形式存在,并且仍与更好编写的代码共存。

那里有一个妥协的元素,比如写原始类型的诱惑。不要那样做。我们有时不得不围绕未经检查的转换跳舞。但它几乎总能完成工作。