how to create an array with Generic type?
Hi,
I need to create a typed array T[] from an object array Object[]. This is due to legacy code integration with older collections.
The method signature is simple:publicstatic <T> T[] toTypedArray(Object[] objects)
I tried using multiple implementations and go over them in the debugger. None of them create a typed collection as far as I can tell. The type is always Object[].
A simple implementation is just to cast the array and return, however this is not so safe.
What is interesting is that if I create ArrayList<String>, the debugger shows the type of the array in the list as String[].
If I create ArrayList<T>, the class contains Object[] and not T[].
I also triedT[] array = (T[]) Array.newInstance(T[].class.getComponentType(), objects.length);
And a few other combinations. All work at runtime, create multiple compilation warnings, and none actually creates T[] array at runtime.
Maybe I am missing something, but Array.newInstace(...) is supposed to create a typed array, and I cannot see any clean way to pass Class<T> into it.T[].class.getComponentType()
Returns something based on object and not on T, and T.class
is not possible.
So is there anything really wrong here, or should I simply cast the array and live with the warnings?
Any help appreciated!
[1449 byte] By [
eli.aa] at [2007-11-27 5:11:59]

# 1
> Maybe I am missing something, but
> Array.newInstace(...) is supposed to create a typed
> array, and I cannot see any clean way to pass
> Class<T> into
> it.
Does it help to pass Class<T> class as parameter of your method like this:public static <T> T[] toTypedArray(Class<T> cls, Object[] objects){
int size = objects.length;
T[] t = (T[])java.lang.reflect.Array.newInstance(cls, size);
System.arraycopy(objects, 0, t, 0, size);
return t;
}
public static void main(String[] args) {
Object[] objects = new Object[] { "1", "2", "3"};
String[] str = toTypedArray(String.class, objects);
}
?
# 2
Arrays of generic types are inherently unsafe in Java. I would avoid using them entirely if possible.
When you talk about having a type T[] at runtime it leads me to believe that you are not aware that generics are (basically) a compile-time only feature in Java. There is never a type T at runtime whether arrays are used or not. The term for this "feature" is 'erasure' and understanding it is key to using generics in Java effectively.
# 3
Thanks for the suggestion.
I tried this approach, and it still does not work. The problem is that you can do that only if the type that goes to T (which is String in this example) is known at compile time.
If it is used by another method, or taken from another class declaration, you will have to do T.class instead of String.class. This is not possible.
So if the main method was in a class with type T, you cannot use it:public class Sample<T> {
public static <T> T[] toTypedArray(Class<T> cls, Object[] objects) {
int size = objects.length;
T[] t = (T[]) java.lang.reflect.Array.newInstance(cls, size);
System.arraycopy(objects, 0, t, 0, size);
return t;
}
public void main() {
Object[] objects = new Object[] {null, null, null};
T[] str = toTypedArray(T.class, objects);
}^^^^^^^
}
eli.aa at 2007-7-12 10:32:35 >

# 4
@dubwai
I am aware that generics are compile time constructs, however there are methods in the Java API that take a typed array T[], and without such array the code will not compile.
Example: Arrays.sort(T[] a, Comparator<? super T> c)
I would also like to have as few warnings as possible at compile time.
eli.aa at 2007-7-12 10:32:35 >

# 5
I'm not sure what kind of solution you expect to find given that you understand that generics are implemented with erasure in Java.
Unless you accept a Class Object at runtime, what could your toTypedArray return other than Object[] given that it has to determine the type of the returned array at compile time?
# 6
> Thanks for the suggestion.
> I tried this approach, and it still does not work.
> The problem is that you can do that only if the type
> that goes to T (which is String in this example) is
> known at compile time.
If your array is not empty, you could obtain Class<T> class from the array element.public static <T> T[] toTypedArray(Object[] objects){
int size = objects.length;
if(size != 0){
T[] t = (T[]) java.lang.reflect.Array.newInstance(objects[0].getClass(), size);
System.arraycopy(objects, 0, t, 0, size);
return t;
}
return null;
}
# 7
> If your array is not empty, you could obtain> Class<T> class from the array element.What happens if the first element is a HashMap and the second is a TreeMap, Hashtable or even a String?
# 8
> What happens if the first element is a HashMap and> the second is a TreeMap, Hashtable or even a String?My code gave me the following:Exception in thread "main" java.lang.ArrayStoreException: java.util.ArrayList
# 9
Ok. May be you could keep information about generic type in the your class:
public class Util {
public static <T> T[] toTypedArray(Class<T> cls, Object[] objects){
int size = objects.length;
T[] t = (T[]) java.lang.reflect.Array.newInstance(cls, size);
System.arraycopy(objects, 0, t, 0, size);
return t;
}
}
public class Sample<T> {
Class<T> cls;
T[] array;
public Sample(Class<T> cls) {
this.cls = cls;
}
public void setArray(Object[] objects){
array = Util.toTypedArray(cls, objects);
}
public T[] getArray(){
return array;
}
public static void main(String[] args) {
Object[] objects = new Object[] { new LinkedList(), new ArrayList()};
Sample<List> myClass = new Sample<List>(List.class);
myClass.setArray(objects);
for(List elem: myClass.getArray()){
System.out.println(elem.getClass().getName());
}
}
}
# 10
So, the main idea is to obtain Class<T> class from the Sample<T> class:
public class Sample<T> {
Class<T> getGenericClass(){
// return Class<T> class
}
public void setArray(Object[] objects){
int size = objects.length;
T[] t = (T[]) java.lang.reflect.Array.newInstance(getGenericClass(), size);
}
}
# 11
I really don't see what the problem is with using a Object[]. How is this any less safe than anything else using generics? ArrayList uses an Object[] internally. The only time I can see the actual runtime type of the array being a concern is if an API specifies a more specific type. This should be known at compile-time, however.
# 12
Yes, the main idea is to get the class of T, getGenericClass(). This is not possible as far as I can tell.
As for using Object[] everywhere, if we do that, we do not need generics at all. The point of generics for us is that it prevents a lot of runtime errors at compile time. If we use object everywhere, it is not possible to catch those errors at compile time.
We are working on a big project on a production site, and so far there has been no single error of type conversion, which before generics I used to see plenty.
Java is a complied language and strongly typed, so I think it is appropriate to take advantage of it for reducing those errors. Otherwise just use PERL :)
eli.aa at 2007-7-12 10:32:35 >

# 13
> Yes, the main idea is to get the class of T,
> getGenericClass(). This is not possible as far as I
> can tell.
>
> As for using Object[] everywhere, if we do that, we
> do not need generics at all. The point of generics
> for us is that it prevents a lot of runtime errors at
> compile time. If we use object everywhere, it is not
> possible to catch those errors at compile time.
I mean cast the Object[] to whatever generic type you need. Take some medication for OCD, accept/suppress the warning and move on with your life.
In the end, the generic array can never be truly typesafe. It's just flaw in Java that cannot be fixed without changing the variance rules of arrays.