了解Java中的类型擦除

时间:2018-05-04 13:03:30

标签: java oop generics type-erasure

我在JShell中尝试了以下Java代码。

class X<A> {
  A id(A a) {
    return a;
  }
}

案例1

X<Integer> w = new X<Integer>();
w.id(5)

在这种情况下,JShell打印只有5,正如我所料。我希望w中的identity函数可以使用类型Integer进行参数化,因此只需要Integer。提供非整数子类型的变量会导致此函数出错。

案例2

X x = new X<Integer>();
x.id(5)

JShell输出5,但是同时输出5,它也会输出以下错误信息:

|  Warning:
|  unchecked call to id(A) as a member of the raw type X
|  x.id(5)
|  ^-----^

对id(A)的未经检查的调用是什么意思?它似乎不会将x的类型推断为X<Integer>,因为我还能够运行x.id("5"),只有一个警告,这在案例1中是不可能的。这意味着x中的身份函数是多态的(相对于提供的变量类型)?

案例3

X y = new X<>()
y.id(5)
X z = new X()
z.id(5)

这种情况与案例2相同。但是,我无法围绕代码进行思考。 y的参数化类型是什么?对象y和z是否与它们是两个独立的对象相同?

我猜测类型擦除的概念在这方面起了作用,但我无法真正理解它并解释上述现象。

2 个答案:

答案 0 :(得分:2)

在Java泛型中,您必须在变量类型上指定泛型类型,而不是其实现。

这意味着您必须拥有

X<Integer> x = new X<>();

X<Integer> x = new X<Integer>();

表示要注册的类型。从Java 7开始,您被允许使用菱形运算符,这意味着编译器能够从数据类型推断出实现类型。如果你有

X x = new X<Integer>();

然后java假定泛型类型只是一个Object。这就是你获得未经检查的赋值的原因,因为就编译器而言,X包含Objects而不是Integers。

案例3与案例2几乎相同,实现是运行时特性,因此编译器始终假定它是一个对象。案例3基本上与Java 5中的内容相同。

答案 1 :(得分:-1)

首先。

X<Integer> x = new X<Integer>();

不同
X x = new X<Integer>();

好。它是tecnically,但是通过删除左侧的类型,您告诉编译器该变量没有特定类型。 我举个例子。

List<Integer> l = new ArrayList<>();
l.add(1); //Everything fine
List l = new ArrayList<Integer>();
l.add(1); //Compiler or Runtimeerror

在Java中,变量的左侧部分是最重要的(在Java10中不再有效),这就是为什么如果以这种方式声明变量,你必须在左侧告诉他他的类型(在Java 10中这是你不再关心你在左侧使用“var”),在右侧你可以使用钻石类型&lt;&gt;