[英]Type erasure Generics
编译时在 new T[5] 处发生错误 => error: generic array creation 根据我的理解,该数组是在编译时创建的,因为我们在编译时不知道 T 的类型我们不能实例化一个数组。 但是,如果 T 在编译时被擦除并更改为 Object 那么为什么仍然会出现此错误? 因为我们可以创建一个 Object 的数组。
// Before Compiling
public class GenericClass<T> {
GenericClass(){
T[] obj = new T[5];
}
}
// After Compiling
public class GenericClass {
GenericClass() {
Object[] obj = new Object[5];
}
}
类似的情况,比如,
public class GenericClass<T> {
GenericClass(){
T obj = new T(); }}
/* error :required: class
found: type parameter T
where T is a type-variable:
T extends Object declared in class GenericClass*/
根据我的理解,数组是在编译时创建的
不,数组是在运行时创建的。
并且因为我们在编译时不知道 T 的类型,所以我们不能实例化一个数组。
正确的。
但是,如果 T 在编译时被擦除并更改为 Object 那么为什么仍然会出现此错误?
因为“它在编译时被擦除并更改为对象”被过于简单化了。
此外,generics 和 arrays 彼此不友好。 问题是,在 generics 部分被擦除的地方,arrays不会那样工作。 你可以这样做:
String[] x = new String[10];
tellMeTheTypeOfMyArray(x);
void tellMeTheTypeOfMyArray(Object[] o) {
System.out.println("Your array's component type is: " + o.getClass().getComponentType());
}
此代码将编译并正常工作,没有错误,并打印:
Your array's component type is: java.lang.String
与 generics 相比,您不能在其中编写此类方法。 你不可能这样做:
List<String> x = new ArrayList<String>();
tellMeTheTypeOfMyList(x);
void tellMeTheTypeOfMyList(List<?> o) {
System.out.println("Your list's component type is: " + ??????);
}
工作。 这里不可能有 java 代码,没有什么可以代替 ?????? 打印 String,因为该信息在运行时根本不存在了。
想象一下这段代码:
// This is written by Wahab today.
class Example<T> {
protected T[] arr;
Example() {
this.arr = new T[10];
}
}
它像你想要的那样工作。 然后我做:
// Written by me, a year later
class Uhoh extends Example<String> {
Uhoh() {
super();
}
void hmmm() {
System.out.println(this.arr.getComponentType());
}
}
我显然期望,不,要求 - 这会打印java.lang.String
,但它不可能这样做。 因为这很奇怪和令人困惑,java 有一个规则:如果你编译你的代码并且你没有看到任何关于 generics 问题的警告(并且没有@SuppressWarnings
将它们消除),那么这种混淆不太可能发生。
允许您编写new T[]
并以这种愚蠢的方式编写new Object[]
被认为太过分了。
与java.util.ArrayList
相同的方式: Do not use generics here 。 Arrays 如果您打算在通用代码中创建它们,则几乎不应该有T
类型。 如果你的代码库中的任何地方都有一个T[]
,那么这意味着你永远不应该为它做任何事情——让你new
代码的调用者为你做。 如果您确实想自己new
new arrays,请不要使用 T,使用Object[]
作为类型,并在需要时转换为 T。 这就是 java 内置 ArrayList class 的工作原理。 一些摘录直接从其来源复制粘贴:
transient Object[] elementData; // non-private to simplify nested class access
public E get(int index) {
Objects.checkIndex(index, size);
return elementData(index);
}
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
这是一个示例,同样直接来自 ArrayList 的来源(或者更确切地说, java.util.Collection
定义了它,而 ArrayList 继承了它),您让调用者为您提供代码来生成 arrays:
default <T> T[] toArray(IntFunction<T[]> generator) {
return toArray(generator.apply(0));
}
在这里,调用者提供了一个 function,它将一个int
转换为一个T[]
- 它采用了执行new String[10]
的概念并将其转换为一个 function,然后您将其传递给 toArray 方法,该方法将使用它(感觉在这里可以随意忽略它是如何使用它的,这是一个有点奇怪的解决方案。它有效,只是 - 不确定你是否应该学习那部分的课程)。
你像这样使用它:
List<String> listOfStrings = ...;
String[] convertedToArray = listOfStrings.toArray(String[]::new);
Java arrays 在运行时知道它们的组件类型。 创建数组时,必须在运行时提供组件类型。 但是在您的GenericClass
中,它不能这样做,因为它在运行时不知道T
是什么。 如果它创建一个Object[]
,那么 object 将具有错误的运行时 class,并且如果T
不是Object
,则该实例与类型T[]
不兼容。 你是对的,在 class 中,没有立即出错。 但是如果变量是T[]
的声明暴露给外部 scope,它期望T
是一个更具体的类型,它可能会导致ClassCastException
:
// Before type erasure
class GenericClass<T> {
T[] obj;
GenericClass() {
obj = new T[5]; // if hypothetically you could do this
}
T[] getObj() {
return obj;
}
}
class MyCode {
public static void main(String[] args) {
GenericClass<String> foo = new GenericClass<>();
String[] strings = foo.getObj(); // no casts needed; no warnings
}
}
// After type erasure
class GenericClass {
Object[] obj;
GenericClass() {
obj = new Object[5];
}
Object[] getObj() {
return obj;
}
}
class MyCode {
public static void main(String[] args) {
GenericClass foo = new GenericClass();
String[] strings = (String[]) foo.getObj(); // ClassCastException at runtime
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.