通过未经检查的类型转换在Java中创建泛型数组
如果我有一个泛型类通过未经检查的类型转换在Java中创建泛型数组,java,arrays,generics,casting,type-safety,Java,Arrays,Generics,Casting,Type Safety,如果我有一个泛型类Foo,我不允许按如下方式创建数组: Bar[] bars = new Bar[]; (这将导致错误“无法创建Bar的通用数组”) 但是,正如dimo414在对的回答中所建议的,我可以做到以下几点: Bar[] bars = (Bar[]) new Object[]; (这将“仅”生成一条警告:“类型安全:未选中从对象[]强制转换到条[]”) 在回应dimo414答案的评论中,一些人声称在某些情况下使用此结构可能会导致问题,其他人则认为这很好,因为对数组的唯一引用是条,它已
Foo
,我不允许按如下方式创建数组:
Bar[] bars = new Bar[];
(这将导致错误“无法创建Bar的通用数组”)
但是,正如dimo414在对的回答中所建议的,我可以做到以下几点:
Bar[] bars = (Bar[]) new Object[];
(这将“仅”生成一条警告:“类型安全:未选中从对象[]强制转换到条[]”)
在回应dimo414答案的评论中,一些人声称在某些情况下使用此结构可能会导致问题,其他人则认为这很好,因为对数组的唯一引用是条
,它已经是所需的类型
我有点困惑,在哪种情况下这是可以的,在哪种情况下它会给我带来麻烦。例如,newacct和Aaron McDaid的评论似乎直接相互矛盾。不幸的是,原始问题中的评论流以未回答的“为什么这‘不再正确’?”结束,因此我决定为其提出一个新问题:
如果条形图
-数组仅包含条形图
类型的条目,那么在使用该数组或其条目时是否仍存在任何运行时问题?或者,唯一的危险是,在运行时,我可以从技术上将数组强制转换为其他类型(如String[]
),这将允许我使用Bar
以外的类型的值填充数组
我知道我可以使用
Array.newInstance(…)
,但是我对上面的类型转换构造特别感兴趣,因为,例如,在GWT中,newInstance(…)
-选项不可用。与列表相反,Java的数组类型是具体化的,这意味着对象的运行时类型[]
不同于字符串[]
。所以你写的时候,
Bar[] bars = (Bar[]) new Object[];
您已经创建了一个运行时类型为
Object[]
的数组,并将其“强制”到Bar[]
。我在引号中说“cast”,因为这不是一个真正的checked cast操作:它只是一个编译时指令,允许您将对象[]
分配到Bar[]
类型的变量中。自然,这为各种运行时类型错误打开了大门。它是否真的会产生错误完全取决于你的编程能力和注意力。因此,如果你能胜任,那么就可以去做;如果您不这样做,或者此代码是许多开发人员的大型项目的一部分,那么这是一件危险的事情。除非您希望在该数组中使用某种类型的Bar
(或您初始化泛型类时使用的任何类型)而不是真正的对象,否则一切正常。例如,具有以下方法:
<T> void init(T t) {
T[] ts = (T[]) new Object[2];
ts[0] = t;
System.out.println(ts[0]);
}
它仍然运作良好;但是当您想要真正使用真正的T[]数组(在上面的示例中应该是String[]时):
然后您会遇到一个问题,因为Object[]
和String[]
是两个不同的类,您拥有的是Object[]
,因此会抛出一个ClassCastException
如果尝试有界泛型类型,问题会更快出现:
<T extends Runnable> void init(T t) {
T[] ts = (T[]) new Object[2];
ts[0] = t;
System.out.println(ts[0]);
ts[0].run();
}
void init(T){
T[]ts=(T[])新对象[2];
ts[0]=t;
System.out.println(ts[0]);
ts[0]。运行();
}
作为一个好的实践,尽量避免使用泛型和数组,因为它们不能很好地混合在一起。好的,我已经使用了这个构造一段时间,它可能会非常混乱
我想我的问题的答案是:只要您始终将数组作为泛型处理,一切都可以正常工作。但一旦你尝试用非通用的方法来治疗它,就会有麻烦。让我举几个例子:
- 在
Foo
中,我可以创建如图所示的数组,并且可以很好地使用它。这是因为(如果我理解正确的话)编译器“擦除”了条
-类型,并简单地将其转换为对象
。因此本质上,在Foo
中,您只是在处理一个对象[]
,这很好
- 但是,如果在
Foo
中有这样一个函数,它提供了对阵列的访问:
public Bar[]getbar(Bar){
Bar[]结果=(Bar[])新对象[1];
结果[0]=巴;
返回结果;
}
如果你在其他地方使用它,你可能会遇到一些严重的问题。以下是一些疯狂的例子(大多数都是有道理的,但乍一看似乎很疯狂),你会发现:
String[]bar=new Foo().getbar(“Hello World”)代码>
将导致java.lang.ClassCastException:[Ljava.lang.Object;无法转换为[Ljava.lang.String
for(字符串栏:new Foo().getbar(“Hello World”)
还会导致相同的java.lang.ClassCastException
- 但是
for(对象栏:new Foo().getbar(“Hello World”))
System.out.println((字符串)条);
工作
- 有一个对我来说毫无意义:
stringbar=newfoo().getbar(“Hello World”)[0];
将导致java.lang.ClassCastException,即使我没有将其分配给任何地方的字符串[]
- 甚至
objectbar=newfoo().getbar(“Hello World”)[0];
将导致相同的java.lang.ClassCastException
- 只有
Object[]temp=new Foo().getbar(“Hello World”);
字符串条=(字符串)温度[0];
工作
顺便说一句,它们中没有一个会抛出编译时错误
- 现在,如果您有另一个泛型类,那么:
Baz类{
Bar getFirstBar(Bar-Bar){
Bar[]Bar=new Foo().getbar(Bar);
返回条[0];
}
}
以下几点效果不错:
String bar=new Baz().getFirstBar(“Hello World”);
如果您意识到在类型擦除之后,getbar(…)
-函数实际上返回了一个Objec,那么这大部分都是有意义的
init("asdf");
String[] strings = init("asfd");
<T extends Runnable> void init(T t) {
T[] ts = (T[]) new Object[2];
ts[0] = t;
System.out.println(ts[0]);
ts[0].run();
}
Bar[] bars = (Bar[]) new Object[];
class Foo<Bar> {
Bar[] get() {
return (Bar[])new Object[1];
}
}
void test() {
Foo<String> foo = new Foo<>();
String[] z = foo.get();
}
class Foo<Bar> {
Bar[] bars = (Bar[])new Object[5];
public Bar get(int i) {
return bars[i];
}
public void set(int i, Bar x) {
bars[i] = x;
}
public Bar[] getArray() {
return bars;
}
}
// in some method somewhere:
Foo<String> foo = new Foo<String>();
foo.set(2, "hello");
String other = foo.get(3);
String[] allStrings = foo.getArray();
class Foo {
Object[] bars = new Object[5];
public Object get(int i) {
return bars[i];
}
public void set(int i, Object x) {
bars[i] = x;
}
public Object[] getArray() {
return bars;
}
}
// in some method somewhere:
Foo foo = new Foo();
foo.set(2, "hello");
String other = (String)foo.get(3);
String[] allStrings = (String[])foo.getArray();