I have written a custom RequestWrapper to add cookies in to the request
#Override
public Cookie[] getCookies()
{
Cookie c=new Cookie("iPlanetDirectoryPro", "macleanpinto");
Cookie c1=new Cookie("iPlanetDirectoryPro1", "macleanpinto1");
Collection<Cookie> result = new ArrayList<Cookie>(2);
result.add(c);
result.add(c1);
return (Cookie[]) result.toArray();
}
The above code at Return throws java.lang.ClassCastException. I am not able to be figure out why it does. Since i have created collection of cookies and i am trying return array list of cookies.
Thanks
can you try result.toArray(new Cookie[2]); instead of (Cookie[]) result.toArray();?
Reason being - calling just result.toArray(); returns Object[]
If you see the source code of ArrayList
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
and
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;
}
here the latter, clearly uses Arrays.copyOf passing the type of array
And Object[] can't be cast to Cookie[]
Try using ArrayList.toArray(T[] a).
Example:
return result.toArray(new Cookie[result.size()]);
As stated in the API, ArrayList.toArray() returns an array of type Object. Here's a thread explaining why you can't cast Object[] to String[]. Even though you're using Cookie[] the same principle applies.
ArrayList internally uses an Object[] to store the elements. So, when you use ArrayList#toArray() method, it returns the Object[].
Now, you cannot cast from an Object[] to a Cookie[], b'coz Object is not a subtype, but a supertype of Cookie. For example, consider below code:
Object[] objArray = {"a", 1, new Date()}; // an Object[]
Cookie[] cookieArray = (Cookie[]) objArray; // You don't expect this to run
So, if you want an Cookie[], you have to iterate over the Object[] and manually convert each element to Cookie type, and add it to a new array. That is what ArrayList#toArray(T[]) does.
So, you can change your return statement to:
return result.toArray(new Cookie[result.size()]);
I've taken result.size() to avoid creation of an extra array in the method. You could have passed new Cookie[0] too. But, a new array will be created inside the toArray method.
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 need this code, but i get this error:
Ljava.lang.Object; cannot be cast to java.lang.String
public Object[] getAllKeys (){
return keys.toArray(new Object[keys.size()]);
}
public String[] getNames (){
return ((String[])super.getAllKeys()); <- Error here. Can't cast, why?
}
The type of the array is Object[] so it cannot know that it contains only Strings. It is quite possible to add a non-String object to that array. As a result the cast is not allowed.
You can return Object[] and then cast each of the objects within that array to string. i.e. (String)arr[0] or you can create a new String[] array and copy all the elements over before returning it.
toArray() returns an array of Objects. If you want to create an array of Strings out of it, you will have to do it yourself. For example,
Object [] objects = super.getAllKeys();
int size = objects.size();
String [] strings = new String[size];
for (int i = 0; i < size; i++)
strings[i] = objects[i].toString();
or something similar... Hope this is useful.
Every String is an Object. Every Object is NOT a String.
You cannot do the cast, because even though Object is a base class of String, their array classes Object[] and String[] classes are unrelated.
You can fix this problem by introducing an additional method that allows taking a typed array:
public Object[] getAllKeys (){
return getAllKeys(new Object[keys.size()]);
}
// Depending on your design, you may want to make this method protected
public <T> T[] getAllKeys(T[] array){
return keys.toArray(array);
}
...
public String[] getNames (){
return super.getAllKeys(new String[keys.size()]);
}
This code takes advantage of the other overload of toArray, which accepts a typed array as an argument.
This cannot be done implicitly since the runtime cannot know that the elements in Object[] are all String types.
If you don't want to code a loop yourself, then one way to coerce is to use
String[] myStringArray = Arrays.asList(keys).toArray(new String[keys.length]);
I think that this will happen without any string copies being taken: asList() binds to the existing array data and toArray uses generics which are removed at runtime anyway due to type erasure. So this will be faster than using toString() etc. Don't forget to deal with any exceptions though.
Try the following snippet
Object[] obj = {"Red","Green","Yellow"};
String[] strArray = (String[]) obj; // Casting from Object[] to String[]
I am trying to convert a Set to an Array.
Set<String> s = new HashSet<String>(Arrays.asList("mango","guava","apple"));
String[] a = s.toArray(new String[0]);
for(String x:a)
System.out.println(x);
And it works fine. But I don't understand the significance of new String[0] in String[] a = s.toArray(new String[0]);.
I mean initially I was trying String[] a = c.toArray();, but it wan't working. Why is the need for new String[0].
It is the array into which the elements of the Set are to be stored, if it is big enough; otherwise, a new array of the same runtime type is allocated for this purpose.
Object[] toArray(), returns an Object[] which cannot be cast to String[] or any other type array.
T[] toArray(T[] a) , returns an array containing all of the elements in this set; the runtime type of the returned array is that of the specified array. If the set 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 set.
If you go through the implementing code (I'm posting the code from OpenJDK) , it will be clear for you :
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;
}
The parameter is a result of one of the many well-known limitations in the Java generics system. Basically, the parameter is needed in order to be able to return an array of the correct type.
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.