Say you have an arraylist defined as follows:
ArrayList<String> someData = new ArrayList<>();
Later on in your code, because of generics you can say this:
String someLine = someData.get(0);
And the compiler knows outright that it will be getting a string. Yay generics! However, this will fail:
String[] arrayOfData = someData.toArray();
toArray() will always return an array of Objects, not of the generic that was defined. Why does the get(x) method know what it is returning, but toArray() defaults to Objects?
If you look at the implementation of toArray(T[] a) of ArrayList<E> class, it is like:
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
Problem with this method is that you need to pass array of the same generic type. Now consider if this method do not take any argument then the implementation would be something similar to:
public <T> T[] toArray() {
T[] t = new T[size]; // compilation error
return Arrays.copyOf(elementData, size, t.getClass());
}
But the problem here is that you can not create generic arrays in Java because compiler does not know exactly what T represents. In other words creation of array of a non-reifiable type (JLS §4.7) is not allowed in Java.
Another important quote from Array Store Exception (JLS §10.5):
If the component type of an array were not reifiable (§4.7), the Java Virtual Machine could not perform the store check described in the
preceding paragraph. This is why an array creation expression with a
non-reifiable element type is forbidden (§15.10.1).
That is why Java has provided overloaded version toArray(T[] a).
I will override the toArray() method to tell it that it will return an
array of E.
So instead of overriding toArray(), you should use toArray(T[] a).
Cannot Create Instances of Type Parameters from Java Doc might also be interesting for you.
Generic information is erased at runtime. JVM does not know whether your list is List<String> or List<Integer> (at runtime T in List<T> is resolved as Object), so the only possible array type is Object[].
You can use toArray(T[] array) though - in this case JVM can use the class of a given array, you can see it in the ArrayList implementation:
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
If you look at the Javadoc for the List interface, you'll notice a second form of toArray: <T> T[] toArray(T[] a).
In fact, the Javadoc even gives an example of how to do exactly what you want to do:
String[] y = x.toArray(new String[0]);
The pertinent thing to note is that arrays in Java know their component type at runtime. String[] and Integer[] are different classes at runtime, and you can ask arrays for their component type at runtime. Therefore, a component type is needed at runtime (either by hard-coding a reifiable component type at compile time with new String[...], or using Array.newInstance() and passing a class object) to create an array.
On the other hand, type arguments in generics do not exist at runtime. There is absolutely no difference at runtime between an ArrayList<String> and a ArrayList<Integer>. It is all just ArrayList.
That's the fundamental reason why you can't just take a List<String> and get a String[] without passing in the component type separately somehow -- you would have to get component type information out of something that doesn't have component type information. Clearly, this is impossible.
I can, and will use an iterator instead of making an array sometimes, but this just always seemed strange to me. Why does the get(x) method know what it is returning, but toArray() defaults to Objects? Its like half way into designing it they decided this wasn't needed here??
As the intention of the question seems to be not just about getting around using toArray() with generics, rather also about understanding the design of the methods in the ArrayList class, I would like to add:
ArrayList is a generic class as it is declared like
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
which makes it possible to use Generic methods such as public E get(int index) within the class.
But if a method such as toArray() is not returning E, rather E[] then things start getting a bit tricky. It would not be possible to offer a signature such as public <E> E[] toArray() because it is not possible to create generic arrays.
Creation of arrays happen at runtime and due to Type erasure, Java runtime has no specific information of the type represented by E. The only workaround as of now is to pass the required type as a parameter to the method and hence the signature public <T> T[] toArray(T[] a) where clients are forced to pass the required type.
But on the other hand, it works for public E get(int index) because if you look at the implementation of the method, you would find that even though the method makes use of the same array of Object to return the element at the specified index, it is casted to E
E elementData(int index) {
return (E) elementData[index];
}
It is the Java compiler which at the compile time replaces E with Object
The very first thing you have to understand is what ArrayList own is just an array of Object
transient Object[] elementData;
When it comes to the reason why T[] is fail, it because you can't get an array of generic type without a Class<T> and this is because java's type erase( there is a more explanation and how to create one). And the array[] on the heap knows its type dynamically and you can't cast int[] to String[]. The same reason, you can't cast Object[] to T[].
int[] ints = new int[3];
String[] strings = (String[]) ints;//java: incompatible types: int[] cannot be converted to java.lang.String[]
public <T> T[] a() {
Object[] objects = new Object[3];
return (T[])objects;
}
//ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
Integer[] a = new LearnArray().<Integer>a();
But what you put into the array is just a object which type is E(which is checked by compiler), so you can just cast it to E which is safe and correct.
return (E) elementData[index];
In short, you can't get what don't have by cast. You have just Object[], so toArray() can just return Object[](otherwise, you have to give it a Class<T> to make a new array with this type). You put E in ArrayList<E>, you can get a E with get().
An array is of a different type than the type of the array. It's sort of StringArray class instead of String class.
Assuming, it would be possible, an Generic method toArray() would look like
private <T> T[] toArray() {
T[] result = new T[length];
//populate
return result;
}
Now during compilation, the type T gets erased. How should the part new T[length] be replaced? The generic type information is not available.
If you look at the source code of (for example) ArrayList, you see the same. The toArray(T[] a) method either fills the given array (if the size matches) or creates a new new array using the type of the parameter, which is the array-type of the Generic Type T.
It is possible to create a "generic" array of the given(known) type. Normally I use something like this in my code.
public static <T> T[] toArray(Class<T> type, ArrayList<T> arrList) {
if ((arrList == null) || (arrList.size() == 0)) return null;
Object arr = Array.newInstance(type, arrList.size());
for (int i=0; i < arrList.size(); i++) Array.set(arr, i, arrList.get(i));
return (T[])arr;
}
Related
I'm trying to decide what to do every time I get a Java heap pollution warning when using parameterized varargs such as in
public static <T> LinkedList<T> list(T... elements) {
...
}
It seems to me that if I am confident not to be using some weird casts in my methods, I should just use #SafeVarargs and move on. But is this correct, or do I need to be more careful? Is there apparently correct code that is actually not safe when using parameterized varargs?
Reading about the subject, I notice that the provided examples are quite artificial. For example, the Java documentation shows the following faulty method:
public static void faultyMethod(List<String>... l) {
Object[] objectArray = l; // Valid
objectArray[0] = Arrays.asList(42);
String s = l[0].get(0); // ClassCastException thrown here
}
which is didactic but pretty unrealistic; experienced programmers are not likely to write code doing stuff like this. Another example is
Pair<String, String>[] method(Pair<String, String>... lists) {
Object[] objs = lists;
objs[0] = new Pair<String, String>("x", "y");
objs[1] = new Pair<Long, Long>(0L, 0L); // corruption !!!
return lists;
}
which is again pretty obviously mixing types in an unrealistic way.
So, are there more subtle cases in which heap pollution happens under parameterized varargs? Am I justified in using #SafeVarargs if I am not casting variables in a way that loses typing information, or mixes types incorrectly? In other words, am I justified in treating this warning as a not very important formality?
Good question. This has bothered me quite a while too. There are two things here - you don't care about the actual runtime type of the elements within the array, like the example that you have shown:
public static <T> LinkedList<T> list(T... elements) {
// suppose you iterate over them and add
}
This is where #SafeVarargs is well, safe.
And the second one is where you DO care about the runtime type of the elements within the array (even if so by accident). Arrays, in java, can not be generic, so you can not create a type T [] ts = new T[10], but you can declare a type T[] ts... and because arrays are covariant you can cast an Object[] to a T[] - if you know the types match.
All this becomes interesting when you pass a generic array:
// create a single element "generic" array
static <T> T[] singleElement(T elem) {
#SuppressWarnings("unchecked")
T[] array = (T[]) new Object[] { elem };
return self(array);
}
// #SafeVarargs
static <T> T[] self(T... ts) {
return ts;
}
Invoking this with Integer[] ints = singleElement(1); looks perfectly legal, but will break at runtime, this is where placing #SafeVarargs would be unsafe.
It will break because that cast (T[]) is actually useless and does not enforce any compile time checks. Even if you rewrote that method as:
static <T> T[] singleElement(T elem) {
#SuppressWarnings("unchecked")
T[] array = (T[]) new Object[]{elem};
System.out.println(array.getClass());
return array;
}
it would still not work.
To declare generic arrays T[] in Java is problematic because their type is not known at compile time and as a consequence they can be misused, as the examples in the question show. So the Java compiler issues warnings whenever this is done.
For example, if we declare a generic array as in
T[] tArray = (T[]) new Object[] { 42 };
we get an "unchecked cast" warning.
Besides such casts, the only other way of introducing a generic array into a program is by using a generic varargs. For example, in
void bar() {
foo(new Integer[]{ 42 })
}
void foo(T... args) {
}
Again here a generic array is being introduced, but in a different way than an unchecked cast, so it gets its own specific warning to make sure the user is not misusing it.
Indeed, as long as one is not converting the array to an array of a different type, it seems that using #SafeVarargs should be safe to use, barring atypical type conversions.
The oracle doc says Generics are implemented in java using a technique call type erasure and this is how it works.
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
Insert type casts if necessary to preserve type safety.
Generate bridge methods to preserve polymorphism in extended generic types.
So if I have a Generic class say Container as below:
class Container<T>{
T initialValue;
List<T> valueList=new ArrayList<T>();
public List<T> getValueList(){
return valueList;
}
}
it's equivalent class would look like after being processed by type erasure:
class Container{
Object initialValue;
List valueList=new ArrayList();
public List getValueList(){
return valueList;
}
}
Correct me if a wrong here
Similarly, if a modify the above class as below
class Container<T>{
T initialValue;
List<T> valueList=new ArrayList<T>();
T[] arrayValue;
public Container(T[] array){
arrayValue=array;
}
public List<T> getValueList(){
return valueList;
}
}
won't be this equivalent to???
class Container{
Object initialValue;
List valueList=new ArrayList();
Object[] arrayValue;
public Container(Object[] array){
arrayValue=array;
}
public List getValueList(){
return valueList;
}
}
if this is true then I should also have like this:
T[] arrayValue=new T[10];//Compile time error;
as the above statement would get converted into
Object[] arrayValue=new Object[10];
Need clarity on how type erasure works for Arrays in Java??
You cannot create generic arrays. The reason is that arrays predate Java's generics, and arrays do not use type erasure. So, for example, an Integer[] and a String[] really have different type at runtime. If you write new T[], the compiler doesn't know what kind of array it needs to create.
You can create a fake generic array by doing:
T[] array = (T[]) new Object[10];
But you have to remember that you really created an Object array, not a T array. At run-time it is possible to put non-T instances in it, so only do this if the array is a private field of your class which is never passed to other objects, so that you can control exactly what objects are put in the array.
If you have a Class<T> instance (a so-called type token) you can use Array.newInstance to create a new array with the correct run-time type:
T[] array = (T[]) Array.newInstance(typeToken, 10);
I have been trying to understand Java generics properly.So in this quest I have come accross one principle " Principle of Truth In Advertising", I am tring to understand this in simple language.
The Principle of Truth in Advertising: the reified type of an array must be a subtype
of the erasure of its static type.
I have written sample code .java and .class files as follows.Please go through code and please explain what part(in code) designates/indicates what part of above statement.
I have written comments to I think I should not write description of code here.
public class ClassA {
//when used this method throws exception
//java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
public static <T> T[] toArray(Collection<T> collection) {
//Array created here is of Object type
T[] array = (T[]) new Object[collection.size()];
int i = 0;
for (T item : collection) {
array[i++] = item;
}
return array;
}
//Working fine , no exception
public static <T> T[] toArray(Collection<T> collection, T[] array) {
if (array.length < collection.size()) {
//Array created here is of correct intended type and not actually Object type
//So I think , it inidicates "the reified type of an array" as type here lets say String[]
// is subtype of Object[](the erasure ), so actually no problem
array = (T[]) Array.newInstance(array.getClass().getComponentType(), collection.size());
}
int i = 0;
for (T item : collection) {
array[i++] = item;
}
return array;
}
public static void main(String[] args) {
List<String> list = Arrays.asList("A", "B");
String[] strings = toArray(list);
// String[] strings = toArray(list,new String[]{});
System.out.println(strings);
}
}
Please try to explain in simple language.Please point out where I am wrong. Corrected code with more comments is appreciated.
Thank you all
I refer to Java Generics and Collections as the Book and the Book's authors as the Authors.
I would upvote this question more than once as the Book makes a poor job of explaining the principle IMO.
Statement
Principle of Truth in Advertising:
the reified type of an array must be a subtype of the erasure of its static type.
Further referred to as the Principle.
How does the principle help?
Follow it and the code will compile and run without exceptions
Do not follow it and the code will compile, but throw an exception at runtime.
Vocabulary
What is a static type?
Should be called the reference type.
Provided A and B are types, in the following code
A ref = new B();
A is the static type of ref (B is the dynamic type of ref). Academia parlance term.
What is the reified type of an array?
Reification means type information available at runtime. Arrays are said to be reifiable because the VM knows their component type (at runtime).
In arr2 = new Number[30], the reified type of arr2 is Number[] an array type with component type Number.
What is the erasure of a type?
Should be called the runtime type.
The virtual machine's view (the runtime view) of a type parameter.
Provided T is a type parameter, the runtime view of the following code
<T extends Comparable<T>> void stupidMethod(T[] elems) {
T first = elems[0];
}
will be
void stupidMethod(Comparable[] elems) {
Comparable first = elems[0];
}
That makes Comparable the runtime type of T. Why Comparable? Because that's the leftmost bound of T.
What kind of code do I look at so that the Principle is relevant?
The code should imply assignment to a reference of array type. Either the lvalue or the rvalue should involve a type parameter.
e.g. provided T is a type parameter
T[] a = (T[])new Object[0]; // type parameter T involved in lvalue
or
String[] a = toArray(s); // type parameter involved in rvalue
// where the signature of toArray is
<T> T[] toArray(Collection<T> c);
The principle is not relevant where there are no type parameters involved in either lvalue or rvalue.
Example 1 (Principle followed)
<T extends Number> void stupidMethod(List<T>elems) {
T[] ts = (T[]) new Number[0];
}
Q1: What is the reified type of the array ts is referencing?
A1: Array creation provides the answer: an array with component type Number is created using new. Number[].
Q2: What is the static type of ts?
A2: T[]
Q3: What is the erasure of the static type of ts?
A3: For that we need the erasure of T. Given that T extends Number is bounded, T's erasure type is its leftmost boundary - Number. Now that we know the erasure type for T, the erasure type for ts is Number[]
Q4: Is the Principle followed?
A4: restating the question. Is A1 a subtype of A3? i.e. is Number[] a subtype of Number[]? Yes => That means the Principle is followed.
Example 2 (Principle not followed)
<T extends Number> void stupidMethod(List<T>elems) {
T[] ts = (T[]) new Object[0];
}
Q1: What is the reified type of the array ts is referencing?
A1: Array creation using new, component type is Object, therefore Object[].
Q2: What is the static type of ts?
A2: T[]
Q3: What is the erasure of the static type of ts?
A3: For that we need the erasure of T. Given that T extends Number is bounded, T's erasure type is its leftmost boundary - Number. Now that we know the erasure type for T, the erasure type for ts is Number[]
Q4: Is the Principle followed?
A4: restating the question. Is A1 a subtype of A3? i.e. is Object[] a subtype of Number[]? No => That means the Principle is not followed.
Expect an exception to be thrown at runtime.
Example 3 (Principle not followed)
Given the method providing an array
<T> T[] toArray(Collection<T> c){
return (T[]) new Object[0];
}
client code
List<String> s = ...;
String[] arr = toArray(s);
Q1: What is the reified type of the array returned by the providing method?
A1: for that you need too look in the providing method to see how it's initialized - new Object[...]. That means the reified type of the array returned by the method is Object[].
Q2: What is the static type of arr?
A2: String[]
Q3: What is the erasure of the static type of ts?
A3: No type parameters involved. The type after erasure is the same as the static type String[].
Q4: Is the Principle followed?
A4: restating the question. Is A1 a subtype of A3? i.e. is Object[] a subtype of String[]? No => That means the Principle is not followed.
Expect an exception to be thrown at runtime.
Example 4 (Principle followed)
Given the method providing an array
<T> T[] toArray(Collection<T> initialContent, Class<T> clazz){
T[] result = (T[]) Array.newInstance(clazz, initialContent);
// Copy contents to array. (Don't use this method in production, use Collection.toArray() instead)
return result;
}
client code
List<Number> s = ...;
Number[] arr = toArray(s, Number.class);
Q1: What is the reified type of the array returned by the providing method?
A1: array created using reflection with component type as received from the client. The answer is Number[].
Q2: What is the static type of arr?
A2: Number[]
Q3: What is the erasure of the static type of ts?
A3: No type parameters involved. The type after erasure is the same as the static type Number[].
Q4: Is the Principle followed?
A4: restating the question. Is A1 a subtype of A3? i.e. is Number[] a subtype of Number[]? Yes => That means the Principle is followed.
What's in a funny name?
Ranting here. Truth in advertising may mean selling what you state you are selling.
In
lvalue = rvalue we have rvalue as the provider and lvalue as the receiver.
It might be that the Authors thought of the provider as the Advertiser.
Referring to the providing method in Example 3 above,
<T> T[] toArray(Collection<T> c){
return (T[]) new Object[0];
}
the method signature
<T> T[] toArray(Collection<T> c);
may be read as an advertisement: Give me a List of Longs and I will give you an array of Longs.
However looking in the method body, the implementation shows that the method is not being truthful, as the array it creates and returns is an array of Objects.
So toArray method in Example 3 lies in its marketing campaigns.
In Example 4, the providing method is being truthful as the statement in the signature (Give me a collection and its type parameter as a class literal and I will give you an array with that component type) matches with what happens in the body.
Examples 3 and 4 have method signatures to act as advertisement.
Examples 1 and 2 do not have an explicit advertisement (method signature). The advertisement and the provision are intertwined.
Nevertheless, I could think of no better name for the Principle. That is a hell of a name.
Closing remarks
I consider the statement of the principle unnecessarily cryptic due to use of terms like static type and erasure type. Using reference type and runtime type/type after erasure, respectively, would make it considerably easier to grasp to the Java layman (like yours truly).
The Authors state the Book is the best on Java Generics [0]. I think that means the audience they address is a broad one and therefore more examples for the principles they introduce would be very helpful.
[0] https://youtu.be/GOMovkQCYD4?t=53
Think of it that way:
T[] array = (T[]) new Object[collection.size()]; A new Array is created. Due to language design, the type of T is unkown during runtime. In your example you know for a fact T is String, but the from the viewpoint of the vm T is Object. All casting operations are happening in the calling method.
So in toArray an array Object[] is created. The type parameter is more or less syntactic sugar which has no consequence for the bytecode created.
So why can't an array of objects be casted to an array of strings?
Let's have an example:
void methodA(){
Object[] array = new Object[10];
array[0]=Integer.valueOf(10);
array[1]=Object.class;
array[2]=new Object();
array[3]="Hello World";
methodB((String[])array);
}
void methodB(String[] stringArray){
String aString=stringArray[1]; //This is not a String, but Object.class!
}
If you could cast an array, you'd say "all elements I've added before are of a valid subtype". But since your array is of type Object, the vm can't guarantee the array will always under all circumstances contain valid subtypes.
methodB thinks it deals with an array of Strings, but in reality the array does contain very different types.
The other way around does not work either:
void methodA(){
String[] array = new String[10];
array[0]="Hello World";
methodB((Object[])array);
//Method B had controll over the array and could have added any object, especially a non-string!
System.out.println(array[1]);
}
void methodB(Object[] oArray){
oArray[1]=Long.valueOf(2);
}
I hope this helps a little bit.
Edit: After reading your question again, I think you are mixing to things:
Arrays can't be casted (as I explained above)
The cited sentence does say in plain English: "If you create an array of type A, all elements in this array must be of type A or a of a subtype of A". So if you create an array of Object you can put any java object into to array, but if you create an array of Number the values have to be of type Number (Long, Double, ...). All in all the sentence is rather trivial. Or I didn't understand it either ;)
Edit 2: As a matter of fact you can cast an array to any type you want. That is, you can cast an array as you can cast any type to String (String s=(String)Object.class;).
Especially you can cast a String[] to an Object[] and the other way around. As I pointed out in the examples, this operation introduces potential bugs in great numbers, since reading/writing to the array will likely fail. I can think of no situation where it is a good decision to cast an array. There might be situations (like generalized utility classes) where it seems to be a good solution, but I still would suggest to overthink the design if you find yourself in a situation where you want to cast an array.
Thanks to newacct for pointing out the cast operation itself is valid.
This question already has answers here:
What's the reason I can't create generic array types in Java?
(16 answers)
Closed 7 years ago.
I'm studying for my Java midterm but I have some problems with the reified type. Here there is a class that is wrong, but I cannot understand why. Can someone help me and maybe give me some explanation? The error is, of course, related to the reified type.
class Conversion {
public static <T> T[] toArray(Collection<T> c) {
T[] a = new T[c.size()];
int i = 0;
for (T x: c) a[i++] = x;
return a;
}
}
An array is a reified type. This means that the exact type of the array is known at runtime. So at runtime there is a difference between, for example, String[] and Integer[].
This is not the case with generics. Generics are a compile-time construct: they are used to check the types at compile-time, but at runtime the exact types are not available anymore. At runtime, the type parameters are just Object (or if the type parameter has an upper bound, its upper bound). So at run-time, there is no difference in the type of Collection<String> and Collection<Integer>.
Now, there is a problem when you want to create an array of a type parameter. At runtime it is unknown what T is, so if you write new T[10], the Java runtime doesn't known what kind of array is to be created, a String[] or a Integer[]. That's why you cannot create an array in this way.
There are a few work-arounds, none of which is entirely satisfactory. The usual solution is to create an Object[], and cast it to the kind of array you want:
T[] theArray = (T[]) new Object[size];
However, you have to remember that this is very unsafe. You should only do this if the scope of the created arrow is small, so that you can manually make sure that the array will only contain T instances and will never be assigned to anything that cannot hold it. The following code demonstrates the problem:
public class Foo<T extends Comparable> {
T[] createArray() {
return (T[])new Object[1];
}
public static void main(String... args) {
Foo<String> foo = new Foo<>();
String[] ss = foo.createArray(); // here
}
}
The line marked with here throws an exception, because you are trying to cast an Object[] to a String[]!
If you really need an array of the correct run-time type, you need to use reflection. Obtain a type token (of type Class<T>) of the type you need, and use Array.newInstance(type, cize) to create the array, for example:
public T[] createArray(Class<T> type, int size) {
return (T[]) Array.newInstance(type, size);
}
Reifiable type is defined by the JLS as:
A type is reifiable if and only if one of the following holds:
It refers to a non-generic class or interface type declaration.
It is a parameterized type in which all type arguments are unbounded wildcards (§4.5.1).
It is a raw type (§4.8).
It is a primitive type (§4.2).
It is an array type (§10.1) whose element type is reifiable.
It is a nested type where, for each type T separated by a ".", T itself is reifiable.
See also the notes that follow §4.7, the reasoning is described in great detail.
Your type parameter is none of the above, therefore it is not reifiable. And thus can't be used in an array creation expression:
It is a compile-time error if the ClassOrInterfaceType does not denote
a reifiable type (§4.7). Otherwise, the ClassOrInterfaceType may name
any named reference type, even an abstract class type (§8.1.1.1) or an
interface type.
You can't create an array of a generic type. The compiler likely complains with something like "generic array creation". There's no nice way around this, but there is a way to do this:
public static <T> T[] toArray(Class<T> type, Collection<T> c) {
T[] a = (T[]) Array.newInstance(type, c.size())
…
}
You'll need Class<T> for this, but it does work :)
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);