This is the code: A simple ceneric class and trying to assign an integer to aa[0].
public class GenericTest<T> {
T [] aa = (T[]) new Object[2];
T bb;
public GenericTest(T x, T y) {
aa[0] = x; aa[1] = y;
System.out.println(aa[0] + " " + aa[1]); //OK
}
static public void main(String[] args) {
GenericTest<Integer> ll = new GenericTest<>(1,2);
ll.bb = 1; // OK
ll.aa[0] = 6; // ClassCastException from Object to Integer
}
}
In fact, the exception message is this:
java.lang.ClassCastException:
[Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
It is saying that it can't cast an Object[] to an Integer[].
The root cause of is the initializer in:
T [] aa = (T[]) new Object[2];
That typecast is an unsafe typecast. And indeed the compiler tells you that something is wrong:
$ javac GenericTest.java
Note: GenericTest.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Anyhow ... what is happening is that when you then do this:
ll.aa[0] = 6;
the JVM is trying to cast the ll.aa to an Integer[] ... because that is what the static typing says that it should be. But it isn't an Integer[]. It is an Object[]. Since Object[] is not assignment compatible with an Integer[] that gives you a class cast exception.
(Why is it doing a hidden type cast? Well this is how the JVM ensures runtime type safety in the face of possible unsafe casts and the like!)
How to fix it?
Avoid using T[]. Use List<T> instead.
Unfortunately, if you have to use T[] there is no easy fix. Basically arrays of a generic type parameter are difficult to create. You end up having to pass the Class object for the parameter's actual class as an extra parameter. Something like this:
import java.lang.reflect.Array;
public class GenericTest<T> {
T [] aa;
T bb;
public GenericTest(Class<T> cls, T x, T y) {sy
aa = (T[]) Array.newInstance(cls, 2);
aa[0] = x; aa[1] = y;
System.out.println(aa[0] + " " + aa[1]); //OK
}
static public void main(String[] args) {
GenericTest<Integer> ll = new GenericTest<>(Integer.class, 1, 2);
ll.bb = 1; // OK
ll.aa[0] = 6; // ClassCastException from Object to Integer
}
}
There is still a warning about an unsafe typecast ... but in this case it is safe to suppress the warning.
For Java 8 onwards, there is another solution which involves passing a reference to the array constructor for Integer[]; see Andy Turner's answer. This is cleaner than using reflection and calling Array.newInstance, but you still have to pass an extra parameter to the constructor.
This is what happens when you use generics. Because generics are erased at runtime, compiler still needs to somehow be safe (after erasure) that things work correctly. Let's simplify this:
GenericTest<Integer> ll = new GenericTest<>(1,2);
ll.bb = 1; // OK
System.out.println(ll.aa.getClass());
The last line is going to be translated to:
28: getfield #7 // Field aa:[Ljava/lang/Object;
31: checkcast #42 // class "[Ljava/lang/Integer;"
notice the checkcast. Since your T was resolved as Integer, means that the array must be Integer[] too; when in reality it is Object []. Compiler is trying to warn you btw when you do :
T [] aa = (T []) new Object[2];
because this is unsafe. In general, generic arrays are a major headache in java, imo.
This line:
ll.aa[0] = 6;
fails because the compiler has inserted some casts. Because it knows that ll is a GenericTest<Integer>, it expected aa to be an Integer[].
The compiler inserts a cast to this type before you can do anything with it: it is evaluated identically to:
((Integer[]) ll.aa)[0] = Integer.valueOf(6);
The problem is that ll.a isn't an Integer[], it's an Object[], so this cast fails.
These casts are inserted even when you're doing something that could be applied to "any array", or even "any object": for example, ll.a.toString() would have casts inserted, even though toString is available on all subclasses of Object, i.e. all objects.
T [] aa = (T[]) new Object[2];
As already pointed out by StephenC, this is an unchecked cast. Checked casts are instructions inserted into the bytecode, which statically check that the object is of a particular type. But, because T is a type variable, there is no "particular type" that the compiler can choose to insert a checkcast instruction on that assignment.
The correct way to deal with this is either:
Use a List<T> instead of a T[] as the field type. Generics and arrays don't play nicely together anyway, so you're better off sticking with generics.
You can use a fixed-length array, e.g. Arrays.asList(null, null), to mimick an array, insofar as you can only set the elements, not clear/add/remove etc.
Inject a T[] (or a Supplier<T[]>, or an IntFunction<T[]> etc) into the constructor of the GenericTest, in order that you push the burden of ensuring the correct type onto the caller:
public GenericTest(T x, T y, IntFunction<T[]> arrayFn) {
// ...
aa = arrayFn.apply(2); // Creates an array of the right length.
}
// Then...
GenericTest<Integer> ll = new GenericTest(1, 2, Integer[]::new);
ll.aa[0] = 6;
This will then be type-correct, because aa was created using Integer[]::new, i.e. it's an Integer[]. The casts are still inserted, they just pass the type check.
T [] aa = (T[]) new Object[2];
Because that line is broken. Your compiler warned you when you tried to compile it. As a general rule, if you get a compiler warning you do not understand, then do something else - java compiler warnings are best treated as: "Your code is completely broken and this is not going to work, but for the benefit of e.g. trying to start this application up because you want to test a completely different part of it, we'll just slap this broken stuff in a class file so you can move on, for now. Do not under any circumstances publish this to production until you fully understand this warning first."
In java, generics is a 100% compile-time show: Generics are compiler-checked documentation. At runtime, generics are mostly erased, and the few places where they remain, they are 'comments' as far as the runtime is concerned. The verifier and the runtime system never does any checks, ever. If javac allows it, the generics are now done being useful.
This in sharp contrast to arrays: Arrays are their own object and they DO know their 'component type'. Also, the compiler doesn't do all that much checking on them, but the runtime DOES. If you use some classfile hackery to put an Integer in a List<String>, the runtime will let you. However, you cannot use any amount of hackery to put an Integer in a String[]. It just won't work - you end up with an ArrayStoreException instead.
Because arrays actually know their type, at runtime, Integer[] and Object[] are different types (whereas at runtime, a List is just a List - the runtime doesn't know what generics are).
T is Integer, and that variable is of type T[], therefore, it is Integer[], therefore, any interactions with aa get you an implicit cast to Integer[]. This is 100% analogous to this code:
List<String> list = new ArrayList<String>();
List raw = list; // legal, but gets you a warning.
raw.add(5);
System.out.println(list.get(0));
You can compile it. If you run it, the last line throws ClassCastException which is weird, there is no cast there at all! Ah, but there is. Because the compiler rewrote that for you, into this code (and remember, the runtime does not know generics, which is why it is rewritten like this):
List list = new ArrayList();
List raw = list;
raw.add(Integer.valueOf(5));
String $v = (String) list.get(0);
System.out.println($v);
You'd think: Wait, why cast it? There is a System.out.println(Object) variant? But, that's just the rules. Java will immediately cast that because it can, after all, list is a List<String> which means its get(int idx) method can be assumed to always return strings, so java will cast it as soon as possible even if not needed.
The exact same thing happens in your code: It sees T[], where T is bound to Integer, so, aa is cast to Integer[] even if it wasn't needed. This cast fails at runtime because an object created as new Object[2] is clearly not an Integer[]. After all, I can invoke:
Integer[] x = ...;
System.out.println(x.getClass().getComponentType());
and this code is guaranteed to print java.lang.Integer. If I can write code so that java.lang.Object is printed, that would be broken, and yet that is exactly what would happen.
So how do I fix it?
Do not, ever, write T[]. It's as simple as that.
Arrays are low-level constructs you shouldn't be using, especially if the component type is non-primitive. If you MUST use them, then they should be relegated to internal implementation details, hidden behind a nicer API.
Do the casting in those wrappers.
This is precisely how ArrayList works. ArrayList has an array inside that holds your list elements (hence the name ArrayList). That is defined as Object[] storage = new Object[...];. Not T[] storage = (T[]) new Object[..]. Then, the get method of ArrayList does the casting:
public T get(int idx) {
// check if idx is within 0-size()...
return (T) storage[idx];
}
This cast does nothing (javac emits a warning that it has no idea what T might be and the runtime doesn't either, so nobody can check this), but this will all work at runtime (obviously: ArrayList works fine).
You need to do the same thing, and hide that array, if you must use it. Or better yet, don't do any of this stuff and use List<T> instead. Arrays of non-primitive types are a blight: If you use them, you better have an excellent reason for it, and hide it as much as possible.
This code works (printing the class inside the constructor....) and I don't know why
public class GenericTest<AnyType> {
public AnyType [] aa = (AnyType []) new Object[2];
public AnyType bb;
public GenericTest(AnyType x, AnyType y) {
aa[0]=x; aa[1]=y; System.out.println(aa.getClass());
System.out.println( aa[0]+" "+aa[1]); //OK
}
static public void main(String[] args) {
GenericTest<Integer> ll = new GenericTest<>(1,2);
ll.aa[0]= 6; // ClassCastException from Object to Integer
}
}
Related
I was reading about Generics from ThinkingInJava and found this code snippet
public class Erased<T> {
private final int SIZE = 100;
public void f(Object arg) {
if(arg instanceof T) {} // Error
T var = new T(); // Error
T[] array = new T[SIZE]; // Error
T[] array = (T)new Object[SIZE]; // Unchecked warning
}
}
I understand the concept of erasure and I know that at runtime, there is no type for T and it is actually considered an Object (or whatever the upper bound was)
However, why is it that this piece of code works
public class ClassTypeCapture<T> {
Class<T> kind;
public ClassTypeCapture(Class<T> kind) {
this.kind = kind;
}
public boolean f(Object arg) {
return kind.isInstance(arg);
}
}
Shouldn't we apply the same argument here as well that because of erasure we don't know the type of T at runtime so we can't write anything like this? Or am I missing something here?
In your example, T is indeed erased. But as you pass kind, which is the class object of the given type, it can be perfectly used for the said check.
Look what happens when you use this class:
ClassTypeCapture<String> capture = new ClassTypeCapture<>(String.class);
Here, the class object of String is passed to the given constructor, which creates a new object out of it.
During class erasure, the information that T is String is lost, but you still have
ClassTypeCapture capture = new ClassTypeCapture(String.class);
so this String.class is retained and known to the object.
The difference is that you do have a reference in the second snippet to an instance of java.lang.Class; you don't have that in the first.
Let's look at that first snippet: There is only one instance of Erased as a class. Unlike, say, C templates which look a bit like generics, where a fresh new class is generated for each new type you put in the generics, in java there's just the one Erased class. Therefore, all we know about T is what you see: It is a type variable. Its name is T. Its lower bound is 'java.lang.Object'. That's where it ends. There is no hidden field of type Class<T> hiding in there.
Now let's look at the second:
Sure, the same rule seems to apply at first, but within the context of where you run the kind.isInstance invocation, there's a variable on the stack: kind. This can be anything - with some fancy casting and ignoring of warnings you can make a new ClassTypeCapture<String>() instance, passing in Integer.class. This will compile and even run, and then likely result in all sorts of exceptions.
The compiler, just by doing some compile time lint-style checks, will really try to tell you that if you try to write such code that you shouldn't do that, but that's all that is happening here. As far as the JVM is concerned, the String in new ClassTypeCapture<String>(Integer.class) and the Integer are not related at all, except for that one compile-time check that says: The generics aren't matching up here, so I shall generate an error. Here is an example of breaking it:
ClassTypeCapture /* raw */ a = new ClassTypeCapture<Integer>(String.class);
ClassTypeCapture<Integer> b = a;
this runs, and compiles. And b's (which is the same as a's - same reference) 'kind' field is referencing String.class. The behaviour of this object's f() method is very strange.
we don't know the type of T at runtime
You're missing the point of generics: generics allow the compiler to "sanity check" the types, to make sure they're consistent.
It's tempting to read ClassTypeCapture<T> as "a ClassTypeCapture for type T", but it's not: it's a ClassTypeCapture, with a hint to the compiler to check that all of the method invocations/field accesses/return values involving that reference are consistent with the type T.
To make this more concrete (let's do it with List, that's easier):
List<String> list = new ArrayList<>();
list.add("hello");
String e = list.get(0);
the <String> is an instruction to the compiler to do this:
List list = new ArrayList();
list.add("hello"); // Make sure this argument is a `String`
String e = (String) list.get(0); // Insert a cast here to ensure we get a String out.
At runtime, the "T-ness" isn't known any more, but the ClassTypeCapture (or Object, or String, or whatever) still is. You can ask an Object if it's an instance of an Object, String, ClassTypeCapture, or whatever.
You just can't ask it if it was a ClassTypeCapture<String> at compile time, because that <String> is just a compiler hint, not part of the type.
I'm very new to generic programming in Java.
I don't understand why arrays of generic type can't be created.
T[] a = new T[size]; //why does this give an error?
If generic type means that the generic placeholder Twill be replaced by a class name during run-time, what prevents us from creating an array having generic references?
After a bit of searching, I found a workaround
T[] a = (T[])new Object[size]; //I don't get how this works?
Although I found a solution, I still fail to understand what prevents from creating a generic array.
Suppose I create a function that returns an Object array.
public Object[] foo(){
return new Object[12];
}
And then make the call
String[] a = (String[])foo();
gives a ClassCastException . But why?
Doesn't it look similar to first line of code where I cast Object array into T array?
T[] a = (T[])new Object[size];
If this went without a glitch why didn't that?
Part of the point is to look at it the other way around. You cannot do (String[]) new Object[10]; because an Object array is not a String array. Because
String[] array = new String[10];
array[0] = "foo";
String foo = array[0];
is fine, but
Object[] objectArray = new Object[10];
objectArray[0] = 10;
String[] stringArray = (String[]) objectArray;
String foo = stringArray[0];
...is trying to assign an Integer to a String, which shouldn't be allowed in the first place. So this code fails when you cast the Object[] to a String[]. That code has to throw a ClassCastException somewhere.
This is all the same for Java even before generics were invented in the first place. Accept all that first. Then move on to generics.
Now, the way Java generics are implemented means that when you compile the code, T is silently rewritten to Object. So T[] array = (T[]) new Object[10] is silently allowed, because it actually gets rewritten to Object[] array = new Object[10]. But as soon as you take it out, things go wrong. For example,
private static <T> T[] newArray() {
return (T[]) new Object[10];
}
if you call String[] array = newArray(), you'll get a ClassCastException at the call site, not within newArray(). This is why Java gives you a warning at (T[]) new Object[10], and that warning may well lead to a real ClassCastException later on.
Generally speaking, don't mix arrays and generics. The way around all this is to use a List properly.
There are several things to note when dealing with arrays.
First, arrays are considered to be covariant; that is, a typed array will maintain its inheritance chain. So, an Integer[] is an Object[] in the same fashion that an Integer is an Object.
This is why your last example fails. You want to cast an Object[] to a String[] through foo:
String[] a = (String[])foo();
An Object[] will never be a String[] since an Object isn't a String (but the opposite will always be true).
Second, arrays and generics don't mix all that well. Generics are considered to be invariant; that is, they would not maintain their inheritance chains. A List<Integer> is not considered to be the same as a List<Object>.
As to why your particular example fails, this is due to type erasure at compile time. Arrays are required to know their concrete type at compile time, and without this information, they cannot be instantiated. Since generics don't store that information, you can't instantiate a generic array in the same way you would instantiate a non-generic array.
That is to say, you must use the cast form:
T[] a = (T[]) new Object[size];
You can read a bit more about generic arrays in this answer, as it covers most of the main points that you would need to know when dealing with them.
Arrays know their type at runtime. A String[] knows it is an array of Strings.
In contrast to this, generic type parameters are erased at runtime, so a List<String> at runtime is just a List.
Since type parameters T are not available at runtime new T[10] (which doesn't compile) could not possibly create a true T[].
It is not true that
T[] a = (T[])new Object[size];
can't throw an exception. It can. Louis Wasserman's example shows that it can cause an exception at the call site, but that line can also throw an exception directly. For example
public static void main(String[] args) {
foo();
}
static <T extends Number> void foo() {
T[] array = (T[]) new Object[42];
}
Here, the lower bound of T is Number, so at runtime, it is attempted to cast an Object[] to a Number[], which throws a ClassCastException.
You can create a T[] if you have a Class<T> object clazz, using for example
Array.newInstance(clazz, length);
im only a beginner and I'd like a good soul to help me ;)I got this method and on the line:
( (HashSet<String>) pos[targetPos]).add(word);
it gives me an exception
(Unchecked cast from Object to HashSet<String>)
I tried to change Object[pos] to String[pos] to be more specific but it then gives me an error on this line: pos[targetPos] = new HashSet<String>();
Type mismatch: cannot convert from HashSet<String> to String
this is the method:
public void add(String word, Object[] root){
Object[] pos = root;
int wordIndex = 0;
int targetPos;
if(word.length()>=3){
for(int i = 1; i <=3; i++){
targetPos = word.charAt(wordIndex) -'a'; //convert a letter into index eg a==0
if(i==3){
if(pos[targetPos]==null){
pos[targetPos] = new HashSet<String>();
}
( (HashSet<String>) pos[targetPos]).add(word);
//System.out.println(Arrays.toString(pos));
break;
}//end if outer if
else{
if(pos[targetPos]==null){
pos[targetPos] = new Object[28];
}
wordIndex++;
pos = (Object[]) pos[targetPos];
}
}//end of for
}
}
The root is
Object[] root = new Object[28];
pos[] is defined as an Object array. When you cast it to HashSet<String> later, Java doesn't know that you can do that. That's what an unchecked cast is -- the compiler is warning you that you MIGHT be doing something unsafe.
You can make the warning go away by changing the type of pos to HashSet<String>[].
The "unchecked cast" message is a warning. The compiler is warning you that it cannot be sure that the explicit cast from Object to HashSet<String> can happen safely during runtime, which means that it is possible that if your array of type Object contains something other than HashSet<String>, you will get a ClassCastException during runtime when the JVM attempts to cast that object into type HashSet<String>. Essentially the compiler is warning you beforehand that you are doing something that is potentially unsafe and it can be a source of problems later.
It is not a good practice to simply use a raw object of arrays. If you are going to be sure that the array is only going to contain HashSet<String> objects, then you should probably type it as such (i.e., Set<String>[]; use the interface instead of the concrete implementation for your type because then you can switch implementations when you want). The only time you should do an explicit cast is when you can be absolutely sure that the object that you are casting is most definitely of the type that you are casting it to. For example, if you have an object that implements an interface, and also assume that you are in some class that is most definitely working with a specific concrete-implementation of that interface. In this case, it is acceptable to cast it from that interface into the concrete type.
Your method should look like this:
public void add(String word, Set<String>[] root){
Set<String>[] pos = root; //You probably don't even need this since you have "root"
...
}
Also, consider using a List of Set<String> instead of an array:
public void add(String word, List<Set<String>> root){
List<Set<String>> pos = root; //You probably don't even need this since you have "root"
...
}
I don't think I really understand Java generics. What's the difference between these two methods? And why does the second not compile, with the error shown below.
Thanks
static List<Integer> add2 (List<Integer> lst) throws Exception {
List<Integer> res = lst.getClass().newInstance();
for (Integer i : lst) res.add(i + 2);
return res;
}
.
static <T extends List<Integer>> T add2 (T lst) throws Exception {
T res = lst.getClass().newInstance();
for (Integer i : lst) res.add(i + 2);
return res;
}
Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - incompatible types
required: T
found: capture#1 of ? extends java.util.List
For the second method to compile, you have to cast the result of newInstace() to T:
static <T extends List<Integer>> T add2 (T lst) throws Exception {
T res = (T) lst.getClass().newInstance();
for (Integer i : lst) res.add(i + 2);
return res;
}
Regarding the difference between the two methods, let's forget about the implementation, and consider only the signature.
After the code is compiled, both methods will have exactly the same signature (so the compiler would give an error if the have the same name). This happens because of what is called type erasure.
In Java, all the type parameters disappear after compilation. They are replaced by the most generic possible raw type. In this case, both methods will be compiled as List add2(List).
Now, this will show the difference between the two methods:
class Main {
static <T extends List<Integer>> T add1(T lst) { ... }
static List<Integer> add2(List<Integer> lst) { ... }
public static void main(String[] args) {
ArrayList<Integer> l = new ArrayList<Integer>();
ArrayList<Integer> l1 = add1(l);
ArrayList<Integer> l2 = add2(l); // ERROR!
}
}
The line marked as // ERROR! won't compile.
In the first method, add1, the compiler knows that it can assign the result to a variable of type ArrayList<Integer>, because the signature states that the return type of the method is exactly the same as that of the parameter. Since the parameter is of type ArrayList<Integer>, the compiler will infer T to be ArrayList<Integer>, which will allow you to assign the result to an ArrayList<Integer>.
In the second method, all the compiler knows is that it will return an instance of List<Integer>. It cannot be sure that it will be an ArrayList<Integer>, so you have to make an explicit cast, ArrayList<Integer> l2 = (ArrayList<Integer>) add2(l);. Note that this won't solve the problem: you are simply telling the compiler to stop whining and compile the code. You will still get an warning (unchecked cast), which can be silenced by annotating the method with #SuppressWarnings("unchecked"). Now the compiler will be quiet, but you might still get a ClassCastException at runtime!
The first one is specified to accept a List<Integer> and return a List<Integer>. List being an interface, the implication is that an instance of some concrete class that implements List is being passed as a parameter and an instance of some other concrete class that implements List is returned as a result, without any further relationship between these two classes other than that they both implement List.
The second one tightens that up: it is specified to accept some class that implements List<Integer> as a parameter, and return an instance of exactly that same class or a descendant class as the result.
So for example you could call the second one like so:
ArrayList list; // initialization etc not shown
ArrayList result = x.add2(list);
but not the first, unless you added a typecast.
What use that is is another question. ;-)
#Bruno Reis has explained the compile error.
And why does the second not compile, with the error shown below.
The error shown is actually reporting that you have tried to run code that failed to compile. It is a better idea to configure your IDE to not run code with compilation errors. Or if you insist on letting that happen, at least report the actual compilation error together with the line number, etc.
"I don't think I really understand Java generics."
Nobody does...
The issue is related to the interesting return type of getClass(). See its javadoc. And this recent thread.
In both of your examples, lst.getClass() returns Class<? extends List>, consequently, newInstance() returns ? extends List - or more formally, a new type parameter W introduced by javac where W extends List
In your first example, we need to assign W to List<Integer>. This is allowed by assignment conversion. First, W can be converted to List because List is a super type of W. Then since List is raw type, the optional unchecked conversion is allowed, which converts List to List<Integer>, with a mandatory compiler warning.
In the 2nd example, we need to assign W to T. We are out of luck here, there's no path to convert from W to T. It makes sense because as far as javac knows at this point, W and T could be two unrelated subclass of List.
Of course, we know W is T, the assignment would have been safe if allowed. The root problem here, is that getClass() loses type information. If x.getClass() returns Class<? extends X> without erasure, both of your examples will compile without even warning. They indeed are type safe.
Generics are a way to guarantee type safety.
Eg:
int[] arr = new int[4];
arr[0] = 4; //ok
arr[1] = 5; //ok
arr[2] = 9; //ok
arr[3] = "Hello world"; // you will get an exception saying incompatible
types.
By default arrays in Java are typeSafe. An integer array is only meant to
contain integer and nothing else.
Now:
ArrayList arr2 =new ArrayList();
arr2.add(4); //ok
arr2.add(5); //ok
arr2.(9); //ok
int a = arr2.get(0);
int b = arr2.get(1);
int c = arr3.get(2);
You willa gain get an exception like what it is not possible to cast Object
instance to integer.
The reason is that ArrayList stores object and not primitive like the
above array.
The correct way would be to explicitly cast to an integer.You have to do this
because type safety is not yet guaranteed.
eg:
int a = (int)arr2.get(0);
To employ type safety for collections, you simply specify the type of objects that your collection contains.
eg:
ArrayList<Integer> a = new ArrayList<Integer>();
After insertion into the data structure, you can simply retrieve it like you
would do with an array.
eg:
int a = arr2.get(0);
What's the reason why Java doesn't allow us to do
private T[] elements = new T[initialCapacity];
I could understand .NET didn't allow us to do that, as in .NET you have value types that at run-time can have different sizes, but in Java all kinds of T will be object references, thus having the same size (correct me if I'm wrong).
What is the reason?
It's because Java's arrays (unlike generics) contain, at runtime, information about its component type. So you must know the component type when you create the array. Since you don't know what T is at runtime, you can't create the array.
Quote:
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 {
public static void 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.
-- gafter
(I believe it is Neal Gafter, but am not sure)
See it in context here: http://forums.sun.com/thread.jspa?threadID=457033&forumID=316
By failing to provide a decent solution, you just end up with something worse IMHO.
The common work around is as follows.
T[] ts = new T[n];
is replaced with (assuming T extends Object and not another class)
T[] ts = (T[]) new Object[n];
I prefer the first example, however more academic types seem to prefer the second, or just prefer not to think about it.
Most of the examples of why you can't just use an Object[] equally apply to List or Collection (which are supported), so I see them as very poor arguments.
Note: this is one of the reasons the Collections library itself doesn't compile without warnings. If this use-case cannot be supported without warnings, something is fundamentally broken with the generics model IMHO.
The reason this is impossible is that Java implements its Generics purely on the compiler level, and there is only one class file generated for each class.
This is called Type Erasure.
At runtime, the compiled class needs to handle all of its uses with the same bytecode. So, new T[capacity] would have absolutely no idea what type needs to be instantiated.
The answer was already given but if you already have an Instance of T then you can do this:
T t; //Assuming you already have this object instantiated or given by parameter.
int length;
T[] ts = (T[]) Array.newInstance(t.getClass(), length);
Hope, I could Help,
Ferdi265
The main reason is due to the fact that arrays in Java are covariant.
There's a good overview here.
I like the answer indirectly given
by Gafter. However, I propose it is wrong. I changed Gafter's code a little. It compiles and it runs for a while then it bombs where Gafter predicted it would
class Box<T> {
final T x;
Box(T x) {
this.x = x;
}
}
class Loophole {
public static <T> T[] array(final T... values) {
return (values);
}
public static void main(String[] args) {
Box<String> a = new Box("Hello");
Box<String> b = new Box("World");
Box<String> c = new Box("!!!!!!!!!!!");
Box<String>[] bsa = array(a, b, c);
System.out.println("I created an array of generics.");
Object[] oa = bsa;
oa[0] = new Box<Integer>(3);
System.out.println("error not caught by array store check");
try {
String s = bsa[0].x;
} catch (ClassCastException cause) {
System.out.println("BOOM!");
cause.printStackTrace();
}
}
}
The output is
I created an array of generics.
error not caught by array store check
BOOM!
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at Loophole.main(Box.java:26)
So it appears to me you can create generic array types in java. Did I misunderstand the question?
From Oracle tutorial:
You cannot create arrays of parameterized types. For example, the following code does not compile:
List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile-time error
The following code illustrates what happens when different types are inserted into an array:
Object[] strings = new String[2];
strings[0] = "hi"; // OK
strings[1] = 100; // An ArrayStoreException is thrown.
If you try the same thing with a generic list, there would be a problem:
Object[] stringLists = new List<String>[]; // compiler error, but pretend it's allowed
stringLists[0] = new ArrayList<String>(); // OK
stringLists[1] = new ArrayList<Integer>(); // An ArrayStoreException should be thrown,
// but the runtime can't detect it.
If arrays of parameterized lists were allowed, the previous code would fail to throw the desired ArrayStoreException.
To me, it sounds very weak. I think that anybody with a sufficient understanding of generics, would be perfectly fine, and even expect, that the ArrayStoredException is not thrown in such case.
In my case, I simply wanted an array of stacks, something like this:
Stack<SomeType>[] stacks = new Stack<SomeType>[2];
Since this was not possible, I used the following as a workaround:
Created a non-generic wrapper class around Stack (say MyStack)
MyStack[] stacks = new MyStack[2] worked perfectly well
Ugly, but Java is happy.
Note: as mentioned by BrainSlugs83 in the comment to the question, it is totally possible to have arrays of generics in .NET
class can declare an array of type T[], but it cannot directly instantiate such an array. Instead, a common approach is to instantiate an array of type Object[], and then make a narrowing cast to type T[], as shown in the following:
public class Portfolio<T> {
T[] data;
public Portfolio(int capacity) {
data = new T[capacity]; // illegal; compiler error
data = (T[]) new Object[capacity]; // legal, but compiler warning
}
public T get(int index) { return data[index]; }
public void set(int index, T element) { data[index] = element; }
}
It is because generics were added on to java after they made it, so its kinda clunky because the original makers of java thought that when making an array the type would be specified in the making of it. So that does not work with generics so you have to do
E[] array=(E[]) new Object[15];
This compiles but it gives a warning.
There surely must be a good way around it (maybe using reflection), because it seems to me that that's exactly what ArrayList.toArray(T[] a) does. I quote:
public <T> T[] toArray(T[] a)
Returns an array containing all of the
elements in this list in the correct order; the runtime type of the
returned array is that of the specified array. If the list fits in the
specified array, it is returned therein. Otherwise, a new array is
allocated with the runtime type of the specified array and the size of
this list.
So one way around it would be to use this function i.e. create an ArrayList of the objects you want in the array, then use toArray(T[] a) to create the actual array. It wouldn't be speedy, but you didn't mention your requirements.
So does anyone know how toArray(T[] a) is implemented?
If we cannot instantiate generic arrays, why does the language have generic array types? What's the point of having a type without objects?
The only reason I can think of, is varargs - foo(T...). Otherwise they could have completely scrubbed generic array types. (Well, they didn't really have to use array for varargs, since varargs didn't exist before 1.5. That's probably another mistake.)
So it is a lie, you can instantiate generic arrays, through varargs!
Of course, the problems with generic arrays are still real, e.g.
static <T> T[] foo(T... args){
return args;
}
static <T> T[] foo2(T a1, T a2){
return foo(a1, a2);
}
public static void main(String[] args){
String[] x2 = foo2("a", "b"); // heap pollution!
}
We can use this example to actually demonstrate the danger of generic array.
On the other hand, we've been using generic varargs for a decade, and the sky is not falling yet. So we can argue that the problems are being exaggerated; it is not a big deal. If explicit generic array creation is allowed, we'll have bugs here and there; but we've been used to the problems of erasure, and we can live with it.
And we can point to foo2 to refute the claim that the spec keeps us from the problems that they claim to keep us from. If Sun had more time and resources for 1.5, I believe they could have reached a more satisfying resolution.
As others already mentioned, you can of course create via some tricks.
But it's not recommended.
Because the type erasure and more importantly the covariance in array which just allows a subtype array can be assigned to a supertype array, which forces you to use explicit type cast when trying to get the value back causing run-time ClassCastException which is one of the main objectives that generics try to eliminate: Stronger type checks at compile time.
Object[] stringArray = { "hi", "me" };
stringArray[1] = 1;
String aString = (String) stringArray[1]; // boom! the TypeCastException
A more direct example can found in Effective Java: Item 25.
covariance: an array of type S[] is a subtype of T[] if S is a subtype of T
T vals[]; // OK
But, you cannot instantiate an array of T
// vals = new T[10]; // can't create an array of T
The reason you can’t create an array of T is that there is no way for the
compiler to know what type of array to actually create.
Try this:
List<?>[] arrayOfLists = new List<?>[4];