由于 Java 泛型的实现,你不能有这样的代码:
public class GenSet<E> {
private E a[];
public GenSet() {
a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation
}
}
如何在保持类型安全的同时实现这一点?
我在 Java 论坛上看到了这样的解决方案:
import java.lang.reflect.Array;
class Stack<T> {
public Stack(Class<T> clazz, int capacity) {
array = (T[])Array.newInstance(clazz, capacity);
}
private final T[] array;
}
但我真的不明白发生了什么。
网友回答:
您可以这样做:
E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];
这是在Effective Java中实现泛型集合的建议方法之一;项目26.没有类型错误,无需重复强制转换数组。但是,这会触发警告,因为它具有潜在危险,应谨慎使用。如评论中所述,这现在伪装成我们的类型,如果使用不安全,可能会导致意外错误或 s。Object[]
E[]
ClassCastException
根据经验,只要强制转换数组在内部使用(例如,支持数据结构),并且不返回或向客户端代码公开,此行为就是安全的。如果需要将泛型类型的数组返回给其他代码,则提到的反射类是正确的方法。Array
值得一提的是,只要有可能,如果你使用泛型,你会有一个更快乐的时间使用s而不是数组。当然,有时你别无选择,但使用集合框架要强大得多。List
网友回答:
我必须反过来问一个问题:您的“已检查”还是“未选中”?
那是什么意思?GenSet
GenSet
Class<E>
E
Collections.checkedCollection
public class GenSet<E> {
private E[] a;
public GenSet(Class<E> c, int s) {
// Use Array native method to create array
// of a type only known at run time
@SuppressWarnings("unchecked")
final E[] a = (E[]) Array.newInstance(c, s);
this.a = a;
}
E get(int i) {
return a[i];
}
}
public class GenSet<E> {
private Object[] a;
public GenSet(int s) {
a = new Object[s];
}
E get(int i) {
@SuppressWarnings("unchecked")
final E e = (E) a[i];
return e;
}
}
请注意,数组的组件类型应该是 type 参数的擦除:
public class GenSet<E extends Foo> { // E has an upper bound of Foo
private Foo[] a; // E erases to Foo, so use Foo[]
public GenSet(int s) {
a = new Foo[s];
}
...
}
所有这些都源于Java中泛型的一个已知且故意的弱点:它是使用擦除实现的,因此“泛型”类不知道它们是在运行时用什么类型参数创建的,因此除非实现某种显式机制(类型检查),否则无法提供类型安全。
网友回答:
下面介绍如何使用泛型来获取您正在寻找的类型的数组,同时保持类型安全(与其他答案相反,它会返回一个数组或在编译时导致警告):Object
import java.lang.reflect.Array;
public class GenSet<E> {
private E[] a;
public GenSet(Class<E[]> clazz, int length) {
a = clazz.cast(Array.newInstance(clazz.getComponentType(), length));
}
public static void main(String[] args) {
GenSet<String> foo = new GenSet<String>(String[].class, 1);
String[] bar = foo.a;
foo.a[0] = "xyzzy";
String baz = foo.a[0];
}
}
编译时没有警告,正如您在 中看到的,对于声明 as 实例的任何类型,您都可以分配给该类型的数组,并且可以将元素从分配给该类型的变量,这意味着数组和数组中的值属于正确的类型。main
GenSet
a
a
它的工作原理是使用类文本作为运行时类型标记,如 Java 教程中所述。编译器将类文本视为 的实例。要使用一个,只需在类的名称后面加上 .所以,充当表示类的对象。这也适用于接口、枚举、任何维度数组(例如)、原语(例如)和关键字(例如)。java.lang.Class
.class
String.class
Class
String
String[].class
int.class
void
void.class
Class
本身是泛型的(声明为 ,其中代表对象所表示的类型),这意味着 的类型是 。Class<T>
T
Class
String.class
Class<String>
因此,每当您调用构造函数时,您都会为表示实例声明类型的数组的第一个参数传递一个类文字(例如 为 )。请注意,您将无法获取基元数组,因为基元不能用于类型变量。GenSet
GenSet
String[].class
GenSet<String>
在构造函数内部,调用该方法会将传递的参数强制转换为由调用该方法的对象所表示的类。调用 中的 static 方法将返回一个数组,该数组的类型由作为第一个参数传递的对象表示,其长度由作为第二个参数传递的对象指定。调用该方法返回一个对象,该对象表示由调用该方法的对象所表示的数组的组件类型(例如 如果对象不表示数组)。cast
Object
Class
newInstance
java.lang.reflect.Array
Object
Class
int
getComponentType
Class
Class
String.class
String[].class
null
Class
最后一句话并不完全准确。调用返回一个表示类的对象,但它的类型是 ,而不是 ,这就是为什么你不能做下面这样的事情。String[].class.getComponentType()
Class
String
Class<?>
Class<String>
String foo = String[].class.getComponentType().cast("bar"); // won't compile
返回对象的每个方法也是如此。Class
Class
关于Joachim Sauer对此答案的评论(我自己没有足够的声誉来评论它),使用强制转换的示例将导致警告,因为编译器在这种情况下无法保证类型安全。T[]
编辑关于Ingo的评论:
public static <T> T[] newArray(Class<T[]> type, int size) {
return type.cast(Array.newInstance(type.getComponentType(), size));
}
模板简介:该模板名称为【如何在Java 泛型的实现保持类型安全的同时实现这一点?】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【Java】栏目查找您需要的精美模板。