I am trying to use java generics-
public T[] toArray()
{
T[] result = (T[])new Object[numberOfEntries];
// some code here to fill the resultarray
return result;
} // end toArray
Now in my main funciton, this is what I am doing-
A<Integer> obj= new A<Integer> ();
obj.add(1); //add method not shown here as it is not relevant to question
obj.add(2);
obj.add(3);
obj.add(4);
Integer arr[] = (Integer[]) obj.toArray();
I get the following error-
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
at tester.main(tester.java:14)
How can I fix this?
You are trying to cast an object of type Object[] to Integer[]. These are incompatible types so the JVM throws a ClassCastException. You need to provide some runtime type information to toArray, for example like this:
public T[] toArray(Class<T> clazz)
{
#SuppressWarnings("unchecked")
T[] result = (T[])Array.newInstance(clazz, numberOfEntries);
// some code here to fill the resultarray
return result;
}
...
// and use it like this:
Integer arr[] = obj.toArray(Integer.class);
Due to type erasure, the toArray() method will return an array of type Object[], not Integer[], and it is not possible to cast Object[] to Integer[].
If you want to return an Integer array at runtime from your generic class, you can try passing in the actual class type as a parameter to the toArray() method:
public T[] toArray(Class<T> c, int size) {
#SuppressWarnings("unchecked")
final T[] result = (T[]) Array.newInstance(c, s);
return result;
}
Usage:
A<Integer> obj= new A<Integer> ();
obj.add(1);
obj.add(2);
obj.add(3);
obj.add(4);
Integer[] arr = obj.toArray(Integer.class, obj.size());
Object[] cannot cast to Integer[] directly. Use below code to move elements one by one:
Object[] arr = new Object[SIZE];
Integer[] arr1 = new Integer[arr.length];
int i = 0;
for(Object a:arr){
arr1[i++] = (Integer)a;
}
You have this line in code T[] result = (T[])new Object[numberOfEntries];
You need to replace it with T[] result = (T[])new T[numberOfEntries];
Related
This question already has answers here:
why does List<String>.toArray() return Object[] and not String[]? how to work around this?
(5 answers)
Closed 3 years ago.
I want to create a static array from a dynamic array of whatever generic type the dynamic array was. I saw List#toArray() which returns Object[] and it doesn't use generics. Is it just safe to cast it to T[] or does the entire array have to be instantiated from the type of class using it?
I went on to try and create my own method in case java didn't provide one but, I got stuck with a compile errors
public static <T> T[] toArray(List<T> list)
{
T[] li = (T[]) Array.newInstance(T.class, list.size());
int index = 0;
for(T obj : list)
{
li[index++] = obj;
}
return li;
}
First of all, you don't need that method. You can use:
List<String> list = new ArrayList<>();
list.add("ff");
list.add("bb");
String[] array = list.toArray (new String[list.size ()]);
In order for your method to work, you have to pass the Class of the generic type parameter:
public static <T> T[] toArray(List<T> list, Class<T> clazz)
{
T[] li = (T[]) Array.newInstance(clazz, list.size());
int index = 0;
for(T obj : list)
{
li[index++] = obj;
}
return li;
}
Then you can call the method with:
String[] array = toArray(list, String.class);
The method proposed by Eran doesn't work if you have a generic element type, because you can't get a Class<List<T>>, say.
Instead, pass an IntFunction<T[]>:
public static <T> T[] toArray(List<? extends T> list, IntFunction<T[]> arraySupplier)
{
T[] li = arraySupplier.get(list.size());
int index = 0;
for(T obj : list)
{
li[index++] = obj;
}
return li;
}
Or, easier, use streams:
return list.stream().toArray(arraySupplier);
Then call like:
String[] array = toArray(list, String[]::new);
List<List<String>> listOfLists = ...
List<?>[] arrayOfLists = toArray(listOfLists, List<?>::new);
Notice that whilst this does support generic array elements, you can only create arrays with a reified element type, so your array type has to be List<?>[]; it still can't be List<String>[].
If your business requirement/Use Case requires an array to be no longer dynamic then you should first create a static array of size equal to your size of dynamic array.
ArrayList<Integer> al = [............] // assuming that ArrayList named al is having some data
int[] arr = new int[al.size()];
// from here you can use a for loop and initialize your static array
for(int i=0; i<arr.length;i++) {
arr[i] = (int) al.get(i); // Unboxing will also be done but still you can type cast to be on safe side
}
// Now you can de-reference the ArrayList object and call garbage collection which will wipe it out of the Heap Memory of your JVM.
al = null; // de-referencing the object by making the reference variable null
System.gc(); // GC happens periodically but to boost performance you can explicitly call it right away.
You can create a method accepting the list of objects and can handle all sorts of arrays using instanceof operator.
I am trying to get Object[] array casted to generic sorted
I've implemented this part of the code
public class SortedArraySet<T extends Comparable<T>> implements Set<T>, Comparator<T> {
T[] arr;
int size = 5, index = 0;
#SuppressWarnings("unchecked")
SortedArraySet() {
arr = (T[]) new Object[5];
System.out.println("New set was initiated");
}
#Override
public int compare(T a, T b) {
if (a.compareTo(b) > 0)
return 1;
else
return 0;
}
When ever I run it I get the following compilation error
Exception in thread "main" java.lang.ClassCastException:
java.base/[Ljava.lang.Object; cannot be cast to
java.base/[Ljava.lang.Comparable; at
q3.SortedArraySet.(SortedArraySet.java:12) at
q3.q3main.main(q3main.java:6)
At line 6 it is stated as follows
SortedArraySet<Integer> sa = new SortedArraySet<Integer>();
The code was used to work fine before I added extends Comparable (and so compareTo) in order to sort the set
And is it possible to use Collections.sort?? I have tried but it doesn't seem to work with an array like that!
You need to use :
arr = (T[]) new Comparable[5];
Instead of
arr = (T[]) new Object[5];
As after Type erasing T[] arr will be Comparable[] arr.So
T[] arr = (T[]) new Object[5];
Will become
Comparable[] arr = (Comparable[]) new Object[5];
And obviously, it will throw java.lang.ClassCastException.
I have the following simple code:
class GenClass<T> {
T method1(T in) {
T[] arr = (T[])new Object[10];
arr[1] = in;
return arr[1];
}
}
public class TestClass {
public static void main(String[] args) {
GenClass<Integer> cl = new GenClass<>();
System.out.println(cl.method1(1000));
Integer[] arr = (Integer[])new Object[10];
arr[1] = 1000;
System.out.println(arr[1]);
}
}
The result is the following:
1000
Exception in thread "main" java.lang.ClassCastException:
[Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
at javaapplication8.TestClass.main(TestClass.java:17)
Java Result: 1
So why this code work well:
T[] arr = (T[])new Object[10];
and this code throw run-time error:
Integer[] arr = (Integer[])new Object[10];
?
Is it because of type erasure?
If so - is "T" at run-time just being exchanged with "Object" in method1 so the source code:
T[] arr = (T[])new Object[10];
became the following at run-time:
Object[] arr = (Object[])new Object[10];
?
Or something different is heppened?
You are correct, generic cast works because of type erasure: Java compiler turns this
T[] arr = (T[])new Object[10];
into
Object[] arr = (Object[])new Object[10];
so the cast succeeds.
Non-generic cast, on the other hand, tries to cast an array of Objects to an incompatible type
Integer[] arr = (Integer[])new Object[10];
thus causing an error.
The rest of generic code works fine, because the assignment of an Integer into Object[] array is allowed. In fact, you can do it without a cast:
Object[] arr = new Object[10];
arr[1] = 1000;
Why does Collection<E>.toArray() (non-parameterized method) return Object[]?
Is it one of those consciously taken decisions? Is there any reason why the toArray() method would not be able to return a E[], if it wanted to?
It's because an array of type T cannot be instantiated without knowing the type Class<T>. Contrast this with toArray(T[] array), which has the following source (example from LinkedList). Notice that the passed-in array is used not only as a possible container, but to possibly instantiate a new array of that type. This code throws an exception if T is not a superclass of E; if objects can't be added to the array.
#SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), size);
int i = 0;
Object[] result = a;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
if (a.length > size)
a[size] = null;
return a;
}
Generics in Java are implemented using a technique called type erasure. This means that an instance of a generic class does not have any information about its generic type. Consider this
List<String> list = new ArrayList<String>();
list.toArray();
since list does not know about its generic type it can create only Object[]
Array containers have an associated item data type, preserved at runtime. If you construct an Object array and then add strings, this object won't be castable to String[]:
Object[] objArr = new Object[] {"a", "b"};
String[] strArr = (String[]) objArr; //Produces ClassCastException
You can also notice how this array property is used at runtime when you add items of an incorrect type to an array:
String[] strArr = new String[] {"a", "b"};
Object[] objArr = (Object[]) strArr; //Legal this time
objArr[0] = 15; //Produces ArrayStoreException
Generic type arguments are erased at runtime, so the JVM doesn't know what specific array type to create at runtime when you call toArray().
If I have a Collection defined as Collection collection = new ArrayList() which contains String instances, how can I convert it to a String[]? collection.toArray() returns an Object[]. Alternatively, how can I instantiate an ArrayList<String> using reflection?
Note that I cannot hardcode String, the method doing this only knows about the Class that it can work with.
Example:
Object test(Class classToCastTo, Object[] values) {
Collection collection = new ArrayList();
for (Object value : values) {
collection.add(classToCastTo.cast(value));
}
return collection.toArray();
}
If I call this with test(String.class, ...), then it will return an Object[]. How can I make it return a String[]?
Use theCollection.toArray((T[])java.lang.reflect.Array.newInstance(theClass, theCollection.size())), where T is the element type. The cast is safe as long as T is an unparameterized type.
If you have the class, you can write a method like this:
public static <T> T[] arrayBuilder(Class<T> classToCastTo, Collection c) {
return (T[]) c.toArray((T[]) Array.newInstance(classToCastTo, 0));
}
Iterate over the Collection and store it in a String array.
Try this example from my code:
Collection c = new ArrayList();
c.add("Vivek");
c.add("Vishal");
String[] arr = new String[ c.size()];
int j = 0;
for (Object s : c){
arr[j] = (String)s;
j++;
}
The following method is what are you looking for
public <T> T[] test(Class<T> classToCastTo, Object[] values) {
Collection<T> collection = new ArrayList<T>();
for (Object value : values) {
collection.add(classToCastTo.cast(value));
}
return collection.toArray((T[])Array.newInstance(classToCastTo, collection.size()));
}
Based on Ben’s answer, the following code snippet works (compiles without warnings and runs):
private static Object test(Class<?> classToCastTo, Object[] values) {
Collection<Object> collection = new ArrayList<Object>();
for (Object value : values) {
collection.add(classToCastTo.cast(value));
}
return collection.toArray(
(Object[]) java.lang.reflect.Array.newInstance(
classToCastTo, collection.size())
);
}
Now you can call the method via
String[] result = (String[]) test(String.class, someValues);
The trick is casting the array that was created via reflection to Object[] so that it satisfies the static type check and matches the parameter type required by toArray.
That said, I don’t understand why you can’t call the method with a generic parameter. If you don’t have a generic type somewhere, the result of this method will be useless anyway.
This seems to do what you need:
public static void main(String[] args) {
Object[] originalArray = {"abc", "def"};
Class clazz = String.class;
Object[] newArray = test(clazz, originalArray);
System.out.println(newArray.getClass()); //class [Ljava.lang.String;
System.out.println(Arrays.toString(newArray)); //[abc, def]
}
static Object[] test(Class classToCastTo, Object[] values) {
Object[] o = (Object[]) Array.newInstance(classToCastTo, values.length);
System.arraycopy(values, 0, o, 0, values.length);
return o;
}
You will get a java.lang.ArrayStoreException if the original array contains something that is not a String.
If you know your collection only contains Strings, this method
public static <T> T[] toArray(Collection collection, Class<T> clazz) {
T[] array = (T[]) Array.newInstance(clazz, collection.size());
return ((Collection<T>) collection).toArray(array);
}
called as
String[] result = toArray(collection, String.class);
will do what you need, though it will give some warnings about unchecked casts.
If you know your collection can only contain strings though, you ought to be able to declare it as a Collection<String> and avoid this sort of mess.