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);
Related
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;
}
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 :)
T[] genericArray= (T[])(new Object[2]);
T has a constraint that it implements Comparable. The line above fails with an exception
" java.lang.ClassCastException: [Ljava.lang.Object;
cannot be cast to [Ljava.lang.Comparable;
How do I initialize a generic array where the T has constraints?
Object doesn't implements Comparable.
Instead of creating it as Object[] create it as Comparable[].
Related to your comment:
A variable can be declared of any type. When you create an array you are not creating objects inside the array, but you are only allocating the memory to store the references to the objects.
So as you can write:
Comparable x = "Pippo"; // Because String is Comparable
you can also write
Comparable[] x = new Comparable[1];
x[0] = "Pippo"; // Here you add a concrete String that is a
// Comparable type on the first position
You get this error because Object does not implement Comparable and thus Object[] is not a sub-type of Comparable[] (which, because of type erasure, is the runtime type of your genericArray).
The underlying problem is that you want to create a generic array. This is not possible in Java. The reason is, that unlike generics, the type of the elements of an array is known at runtime. If you write new T[], it is not known which type of array must be created.
You try to circumvent this by creating an array of some supertype. But this is also not correct (and you should get a warning if you do it). If you create a an array with new Comparable[size], you create a an array of Comparables, not an array of some subtype of Comparable. A T[] might be a String[] or a Long[], and String[] and Long[] are different types than Comparable[] (also at runtime).
To demonstrate the problem, consider the following program:
public class Foo<T extends Comparable> {
T[] createArray() {
return (T[])new Comparable[1];
}
public static void main(String... args) {
Foo<String> foo = new Foo<>();
String[] ss = foo.createArray(); // here
}
}
It might look perfectly okay at first sight, but when your run it, you get a ClassCastException, because in the marked line a Object[] is cast to a String[].
The solution is to use Class<T> objects as so-called type tokens. These let you store the type so that you can access it at run-time. Now you can create an array with the correct type by using Array.newInstance(Class<T>, int...). For example:
public class Foo<T extends Comparable> {
private Class<T> type;
public Foo(Class<T> type) {
this.type = type;
}
T[] createArray() {
return (T[])Array.newInstance(type, 1);
}
public static void main(String... args) {
Foo<String> foo = new Foo<>(String.class);
String[] ss = foo.createArray();
}
}
(You may still get a warning from the compiler, because newInstance's return type is Object. You can ignore it because the object it returns is an array of the correct type.)
This question already has answers here:
How to create a generic array in Java?
(32 answers)
Closed 9 years ago.
How to make following code completely generic?
Why I'm not allowed to write:
private E[]store = new E[length];
import java.util.Arrays;
public class MyArrayList<E> {
private int size = 0;
private int length = 10;
private Object [] store = new Object[length];
public E get(int index){
return (E)store[index];
}
public void add(E item){
if(size >= store.length -5){
increment(store);
}
store[size++] = item;
}
private <T> void increment(T[] store2) {
store = Arrays.copyOf(store2, store2.length * 2);
}
public int size(){
return size;
}
}
Array element type, unlike the generic type parameter, is a reified entity. In other words, new Integer[] creates a different kind of object than new Double[]. As opposed to that, new ArrayList<Integer>() creates exactly the same object as new ArrayList<Double>().
Since the generic type parameter is erased from the runtime code, the JVM is hopeless in instantiating the correct type of array you want it to.
You can only declare array variables whose component type is a type parameter, but you cannot create the corresponding array objects. The type of E is unknown at runtime, due to type erasure. The compiler does not know how to create an array of an unknown component type.
Similarly, you cannot create an array of concrete parameterized type.
A workaround is to use Array.newInstance method:
Class<E> clazz;
E[] array = (E[])Array.newInstance(clazz, length) ;
However, this will give you an Unchecked Cast warning, because Array.newInstance returns an Object, and you are casting it to E[]. You can suppress the warning using #SuppressWarnings annotation. But, you need to pass the clazz argument in that method, which should be the class type of E type parameter.
There is a major difference between an array and generic types - i.e, arrays are reified (As already stated by #Marko in his answer). I would add a paragraph, from the book Effective Java - Item 29 on this topic:
This means that arrays know and enforce their element types at
runtime. As noted above, if you try to store a String into an array of Long, you’ll
get an ArrayStoreException. Generics, by contrast, are implemented by erasure
[JLS, 4.6]. This means that they enforce their type constraints only at compile
time and discard (or erase) their element type information at runtime. Erasure is
what allows generic types to interoperate freely with legacy code that does not use
generics (Item 23).
Because of these fundamental differences, arrays and generics do not mix
well. For example, it is illegal to create an array of a generic type, a parameterized
type, or a type parameter. None of these array creation expressions are legal: new
List<E>[], new List<String>[], new E[]. All will result in generic array creation
errors at compile time.
In Java it is possible to declare an array of type variables, but I'm not able to create the array. Is it impossible?
class ClassName<T> {
{
T[] localVar; // OK
localVar = new T[3]; // Error: Cannot create a generic array of T
}
}
Generic type of array are not there in Java.
You can go for ArrayList
Explanation :
array in java are of covariant type.
Java arrays have the property that there types are covariant , which means that an array of supertype references is a supertype of an array of subtype references.That is, Object[] is a supertype of String[] for example. As a result of covariance all the type rules apply that are customary for sub- and supertypes: a subtype array can be assigned to a supertype array variable, subtype arrays can be passed as arguments to methods that expect supertype arrays, and so on and so forth.Here is an example:
Object[] objArr = new String[10];// fine
In contrast, generic collections are not covariant. An instantiation of a parameterized type for a supertype is not considered a supertype of an instantiation of the same parameterized type for a subtype.That is, a LinkedList<Object> is not a super type of LinkedList<String> and consequently a LinkedList<String> cannot be used where a LinkedList<Object> is expected; there is no assignment compatibility between those two instantiations of the same parameterized type, etc.Here is an example that illustrates the difference:
LinkedList<Object> objLst = new LinkedList<String>(); // compile-time error
Source: http://www.angelikalanger.com/Articles/Papers/JavaGenerics/ArraysInJavaGenerics.htm
T[] localVar = (T[])(new Vector<T>(3).toArray()); // new T[3];
This is only possible in a language with reified generics, like Gosu. Java has type erasure, so the type of T isn't available at runtime.
You can't. You can do it if you have the Class object representing T, you can use java.lang.reflect.Array:
public static <T> T[] createArray(Class<T> clazz, int size) {
T[] array = (T[]) java.lang.reflect.Array.newInstance(clazz, length);
return array;
}
Another way that I got around this is by creating a different class to hold the type-Variable, then create an array of that class
eg.
public class test<T>{
data[] localVar = new data[1];
private class data<E>{
E info;
public data(E e){ info = e; }
}
public void add(T e){ localVar[0] = new data<T>(e); }
}
the code above cant be used for anything practical unless you want to add one item to an array, but its just shows the idea.
You cannot initialize a generic type directly. However, you can create an array of Object type and cast it to generic array type as following:
class ClassName<T> {
{
T[] localVar = (T[]) new Object[3];
}
}