The following construct doesn't seem to be legal:
List<String>[].class
or
List<String>.class
But this doesn't seem to be illegal considering the specs. A legal PrimaryNoNewArray is: Type.class and Type can be a ReferenceType that could potentially be a generic type.
Maybe I missed something, but I don't think the specs are clear on this construct. The early access compiler rejects it.
The specs I read should allow array types. Page 12 gives examples with array of generic types.
I tried to compile this example, but all usage of arrays of generic types are rejected.
import java.util.*;
public class X {
void foo() {
List<String>[] array = null;
System.out.println(array.getClass());
}
public Object get(Class<?>[] params) {
Object o = new Vector<String>[3];
}
}
Arrays of generic types are not allowed because they're not sound. The problem is due to the interaction of Java arrays, which are not statically sound but are dynamically checked, with generics, which are statically sound and not dynamically checked. Here is how you could exploit the loophole:
class Box<T> {
final T x;
Box(T x) {
this.x = x;
}
}
class Loophole {
publicstaticvoid main(String[] args) {
Box<String>[] bsa = new Box<String>[3];
Object[] oa = bsa;
oa[0] = new Box<Integer>(3); // error not caught by array store check
String s = bsa[0].x; // BOOM!
}
}
We had proposed to resolve this problem using statically safe arrays (aka Variance) bute that was rejected for Tiger.
The spec is out of date. I believe that Neal is supposed to be including an updated spec with the next release. You might want to read an article that Angelika Langer posted a while back on the problems with arrays and generics by type erasure:
So my next question is: When will we get an updated spec?
I'd like to know what is legal and what is not. If I cannot rely on the specs, I don't know what I should do.
For now can I assume that any construct with array of generics is illegal and can safely be rejected as parse time?
It worries me that I'm going to have to keep in the back of my mind the rule "Don't store generic types in arrays". It adds complexity to the language, having this extra rule to remember. I understand that this is the best solution that can be put together given the release schedule. However, are there better solutions in the works for later releases? Or is this rule to remain in Java from here on out?
At this point you can still work around the limitations like this, leading to a ClassCastException. Is this just as bad a situation as the other loophole was? Cause just having a ClassCastException thrown seems like an okay consequence to me.
class Array<T> {
T[] array;
Array(int sz) {
array = (T[])(new Object[sz]);
}
void set(int i, T elt) {
array[i]=elt;
}
T get(int i) {
return array[i];
}
}
class Loophole {
publicstaticvoid main(String[] args) {
Array<Box<String>> bsa = new Array<Box<String>>(3);
Object[] oa = bsa.array;
oa[0] = new Box<Integer>(3);
String s = bsa.get(0).x; // ClassCastException
}
}
Another weird thing is that ClassCastExceptions are also thrown whenever you try and modify the array member variable from outside the Array class: for instance
Box<String> bs = bsa.array[0]
and
bsa.array[0] = new Box<String>("Hello")
both throw ClassCastExceptions, whereas the get and set methods, which ostensibly do the same things, work fine. Personally I'm OK with it as long as there is a way of making arrays of generic types; of course it'd be nice if we didn't have to use workarounds like these.
I don't know why the compiler doesn't give you a warning there. It shouldn't think that it is safe to cast to a T[].
The client code fails with a ClassCastException, because that is where the casts for T are generated. That is how erasure works - it can't generate casts in the "generic class", because it wouldn't know what to cast to. So, casts generate them in the client code. Casts in the source code of your generic class (i.e. ( T[] )) just get thrown away.
It seems that in the case of the Array<T> class above the array member is seen after erasure as being of type Object and not an array type at all; the compiler must be inserting some complicated casting within the class for the get and set methods, since the only way I can get direct indexing to function outside is something like
Array<String> stringa = new Array<String>(3);
((Object[])(stringa.array))[0]=(Object)"hello";
String c = (String)((Object[])(stringa.array))[0];
... Of course, given this as the alternative option I'll stick with using get and set methods. Is there some reason why erasure changes the type T[] to Object rather than Object[]?
erasure changes the type T[] to Object rather than Object[]
- ok I'm wrong; javac -s makes everything clear. Sorry for the misinformative previous post.
class Box<T> {
final T x;
Box(T x) {
this.x = x;
}
}
class Loophole {
publicstaticvoid main(String[] args) {
Box<String>[] bsa = new Box<String>[3];
Object[] oa = bsa;
oa[0] = new Box<Integer>(3); // error not caught by array store check
String s = bsa[0].x; // BOOM!
}
}
Seems the root cause is the Runtime knows nothing about generics.
Couldn't Java make Runtime be aware of generics and do not break backward compatibility?
e.g. make Vector<String>.class not equal Vector.class, Vector<Object>.class equal Vector.class, etc. So array store error can be caught.
I am working on a grammar for the JDK 1.5 and I cannot figure out how I should write this rule to get a LALR(1) grammar.
PrimaryNoNewArray ::= Type '.' 'class'
In this case Type can be a PrimitiveType or a ClassOrInterfaceType or an ArrayType. Which means that it could potentially be an array of generic types.
I simply would like to get the rules in the grammar that involve array types. What is actually legal and what is not?
Thanks.
This topic has
20
replies
on
2
pages.
1
|
2
|
Next »