corresponding to the title, my problem is to get the array-length of an generic array, which is inside an instance of an class, which has been initialized with this generic type.
When i want to get the length of my array (ak.array.length), java throws this error:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
at Testclass.main(Testclass.java:15)
So where is my fault in this code?
Thanks in advance
public class Testclass {
static class ArrayKeeper<T> {
protected T[] array;
public ArrayKeeper() {
array = (T[])new Object[5];
}
}
public static void main(String[] args) {
//create a new instance of inner class ArrayKepper (type String)
Testclass.ArrayKeeper<String> ak = new Testclass.ArrayKeeper<String>();
System.out.println(ak.array.length); //cast error here.
}
}
The problem is in this line:
array = (T[]) new Object[5];
That's not the way to create a generic array, you're creating an Object[] and not a String[] as you intended. You can not create an Object[] and then cast it to any type you want, you have to create an array of the correct class. Try this instead:
public ArrayKeeper(Class<T> clazz, int size) {
array = (T[]) Array.newInstance(clazz, size);
}
And then, when you need to instantiate an ArrayKeeper do this:
Testclass.ArrayKeeper<String> ak =
new Testclass.ArrayKeeper<String>(String.class, 5);
I think you need to implement the length getter in ArrayKeeper, just because you can't cast an Object[] to a String[], and calling ak.array does try to do that cast.
Related
Imagine this two sample codes:
public class TestCompile<T> {
private T[] array;
public static void main(String[] args) {
}
}
public class TestNoCompile<T> {
private T[] array = new T[5];
public static void main(String[] args) {
}
}
The first class TestCompile has no errors at compilation time and the second one TestNoCompile is not able to compile.
I understant why the second one doesnt compile since the arrays in Java are covariant and the type erasure is not compatible with that. But I cant understant why the first example compiles, why can I declare a generic array if then I cant initialize it?
On the other hand I cant unserstant this other example:
public class Example<T> {
private T[] array;
public static void main(String[] args) {
Example<Integer> example = new Example<>();
example.method(new Integer[5]);
}
public void method(T[] array) {
array[0] = 1; //This line doesnt compile.
}
Here it seems Im able to initialize a generic array in the method method(...) but then Im not able to store any value in it. Which is the explanation of this behaviour?
There's nothing wrong with the type T[] itself. It's perfectly fine to have a variable of type T[] and you can assign any value of type T[] to that variable fine, without any warnings. The question is how do you get a value of type T[].
I think you've answered your own question later when you showed that, for example, you can have a value of type T[] passed in from the outside, into a method (or a constructor) of your class. And in the caller's scope in your example, T is a concrete type (Integer), so the caller can create a T[] in its scope fine and pass it in.
As you have found, you can't create a value of type T[] (other than null) without a warning inside the class (where T is generic). This is because arrays know their component type at runtime (because arrays in Java check at runtime every element that is stored into the array is an instance of the component type), so to create an array object, you need to provide the component type of the array you want to create at runtime, and inside the class, you don't know what T is at runtime. So new T[5] is not a valid expression.
In Andreas's answer, they create an array of type Object[], and then cast it to T[], but this is basically lying to the compiler. Obviously, if T is any type other than Object, this cast is incorrect. For example, String[] foo = (String[]) new Object[5]; throws a class cast exception at runtime. However, T is erased to Object inside the class, so it does not immediately throw a class cast exception. You get an unchecked cast warning to warn you that you might not get an exception even if the cast is incorrect, so you may have a variable whose compile-time type is incompatible with its runtime type, and you may unexpectedly get a class cast exception somewhere else later. For example, if you have a method that returns the array to the outside of the class as type T[], and the place outside the class has a concrete type for T, it will cause a class cast exception where there is no cast:
public class Example<T> {
private T[] array = (T[]) new Object[5];
public T[] getArray() {
return array;
}
public static void main(String[] args) {
Example<Integer> example = new Example<>();
Integer[] foo = example.getArray(); // class cast exception
}
}
Your statement that you cannot store any value in the array is incorrect. You can store values in it, but you can only store values of type T. Inside the class, you don't know what T is, so where are you going to get a value of type T? You would either have to use null, or you have to get it from outside the class:
public class Example<T> {
private T[] array;
public Example(T[] a) {
array = a;
}
public void set(int i, T x) {
array[i] = x;
}
public static void main(String[] args) {
Example<Integer> example = new Example<>(new Integer[5]);
example.set(0, 1);
}
}
Because of type-erasure, a generic array becomes an Object[] at runtime, so you need to create it as such, cast it, and acknowledge that what you're doing is not safe:
#SuppressWarnings("unchecked")
private T[] array = (T[]) new Object[5];
As for the array[0] = 1 statement, the problem is that array is a T[], and that T can be anything, so the code isn't valid.
What is you changed the code in main as follows?
Example<String> example = new Example<>();
example.method(new String[5]);
The array[0] = 1 statement is now obviously not valid, and remember, declaring a Example<String> could easily be done elsewhere at the same time you have Example<Integer> in main.
The code in method must be value for all possible T's.
I just start learning Generics of Java. It confused me when I try to read some sample examples, just as below. I know it's something related to the type erasure, but I can not figure out what's going wrong.
package graph;
import java.util.*;
public class JustTest<T> {
public static void main (String[] args) {
ArrayQueue<String> q = new ArrayQueue<String>();
q.offer(new String("string_1"));
q.offer(new String("string_2"));
Object[] ob = q.getArray();
System.out.println(ob);
// The lines below went wrong when running
// Error information:
//java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
String[] str = q.getArray();
System.out.println(str);
}
}
class ArrayQueue<T> {
private static final int MINIMUM_SIZE = 1024;
private T[] array = (T[]) new Object[MINIMUM_SIZE];
private int lastIndex = 0;
public T[] getArray () {
return array;
}
public T getElement () {
return array[0];
}
public boolean offer(T value) {
array[lastIndex % array.length] = value;
lastIndex++;
return true;
}
}
The problem lies with the unchecked cast in your ArrayQueue class:
private T[] array = (T[]) new Object[MINIMUM_SIZE];
You basically say here that an Object[] is actually a T[], but this cast is 'unchecked' so there is never a check at runtime. If there was, this cast would fail, since an Object[] is not a T[] unless T is Object. But since the cast is unchecked, the program doesn't crash on this line.
The program crashes on the line where the compiler inserts an implicit cast, at the point where T is a known concrete type:
String[] str = q.getArray();
Here you go from T[] to String[], so the compiler will insert an implicit cast here:
String[] str = (String[]) q.getArray();
And this is where it blows up, since your earlier cast to T[] was faulty.
With the interface you currently have, the most convenient solution would be to pass a reference to a constructor of T[], so you can actually create a T[]:
class ArrayQueue<T> {
private static final int MINIMUM_SIZE = 1024;
private T[] array;
...
public ArrayQueue(IntFunction<T[]> arrayCons) {
array = arrayCons.apply(MINIMUM_SIZE);
}
...
ArrayQueue<String> q = new ArrayQueue<>(String[]::new); // passing a constructor reference
Or use a similar solution to what API Collections do, using an Object[] instead of a T[] array, and require a reference to a T[] constructor when calling getArray:
class ArrayQueue<T> {
private static final int MINIMUM_SIZE = 1024;
private Object[] array = new Object[MINIMUM_SIZE];
...
public T[] getArray(IntFunction<T[]> arrayCons) {
T[] ret = arrayCons.apply(size()); // implement size
System.arrayCopy(array, firstIndex, ret, 0, size());
return ret;
}
public T getElement() {
return (T) array[0]; // a safe unchecked cast, since you only put Ts into the array.
}
}
Update
I missed up something last time, if you are learning generics, just picked up a little twisted example, you probably need to start with some classes like Pair, but well.
I Made this changes to your ArrayQueue class:
The #SuppressWarnings is added to avoid the error casting, because we know that the cast we made here manually is right, so, suppress this warning is fine in this case.
I also added two constructors, one parameter and two parameters, the first parameter is the Class<T> c is the type you will say it will have in the moment you create it, and the second one is if you want another sizes for your array.
in this line:
array = (T[]) Array.newInstance(c, MINIMUM_SIZE);
is how I made possible to create the generic array, with the Array.newInstance method, you can tell the class and the size, and the type is given as parameter, that's the trick.
#SuppressWarnings("unchecked")
class ArrayQueue<T> {
private static final int MINIMUM_SIZE = 1024;
private T[] array;
private int lastIndex = 0;
public ArrayQueue(Class<T> c){
array = (T[]) Array.newInstance(c, MINIMUM_SIZE);
}
public ArrayQueue(Class<T> c, int size){
array = (T[]) Array.newInstance(c, size);
}
public T[] getArray () {
return array;
}
public T getElement () {
return array[0];
}
public boolean offer(T value) {
array[lastIndex % array.length] = value;
lastIndex++;
return true;
}
}
And in the main class, for testing it:
public static void main(String[] args)
{
ArrayQueue<String> q = new ArrayQueue<String>(String.class);
q.offer(new String("string_1"));
q.offer(new String("string_2"));
String[] ob = q.getArray();
System.out.println(ob[0]);
System.out.println(ob[1]);
// The lines below went wrong when running
// Error information:
//java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
String[] str = q.getArray();
System.out.println(str[0]);
}
During runtime, Ts are replaced by Object, unless there is a bounding type. This means that
private T[] array = (T[]) new Object[MINIMUM_SIZE];
actually becomes
private Object[] array = new Object[MINIMUM_SIZE];
So when you call getArray, you get an array of Object, which can't be assigned a reference of type String[]. Hence the ClassCastException.
Each element in the array can, by itself, be cast from Object to String (specifically because it's String), but the array as a whole can't be cast.
Which is why you get
[Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
The [L means it's an array.
To get them as strings, you can do this thing:
String[] str = Arrays.toList(q.getArray()).stream().map(Object::toString()).collect(Collectors.toList()).toArray();
Which should work.
I have below generic method that returns a generic array:
public static <T> T[] genericMethod1(List<T> input) {
T[] res = (T[]) new Object[input.size()];
int i = 0;
for (T t : input) {
res[i] = t;
i++;
}
return res;
}
public static <T> T genericMethod2(List<T> input) {
return input.get(0);
}
But later when I try to get the result array with:
LinkedList<Integer> list = new LinkedList<Integer>();
list.addFirst(1);
list.addFirst(1);
Integer[] i = (Integer[]) genericMethod1(list); // 1) Runtime error
Integer j = genericMethod2(list); // 2) works
For case 1, I always get error at runtime:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
Anybody can explain why and how to return the generic array properly? Thanks.
Below is my understanding, please correct me if I'm wrong.
As Tim mentioned, type erasure happened at compile time, so in bytecode, each T object is just type Object, meanwhile, compiler will add type cast from Object to T "properly".
Say T is an Integer, where T is declared, it's Object. For where it's referred, it's type cast (implicitly) to T.
EXCEPT that if T[] array is declared, it's Object[], and where the array is referred, it stays Object[]. No implicit cast to T[] happens.
The explanation for what you are seeing is due to something called type erasure. Here is what your genericMethod() will look like after the compiler performs type erasure:
public static Object[] genericMethod(List input) {
Object[] res = new Object[input.size()];
int i = 0;
for (Object t : input) {
res[i] = t;
i++;
}
return res;
}
In other words, this method will return an array of type Object. There is no way to cast an Object[] to an Integer[] because they are not the same type. If you want your method to be able to dynamically return the type you want, then you can use Array.newInstance(). This will require also passing in the type of the array you want as an input parameter:
public static <T> T[] genericMethod(Class<T> clazz, List<T> input) {
#SuppressWarnings("unchecked")
T[] res = (T[]) Array.newInstance(clazz, input.size());
int i = 0;
for (T t : input) {
res[i] = t;
i++;
}
return res;
}
Now your code snippet will run without error:
LinkedList<Integer> list = new LinkedList<Integer>();
Integer[] i = genericMethod(Integer.class, list);
Update:
Your second method, genericMethod2(), will look like this after type erasure:
public static Object genericMethod2(List input) {
return input.get(0);
}
It will return the first element of the input list, cast to Object. Here is your usage of that method:
Integer j = genericMethod2(list);
The compiler will try to cast the output from genericMethod2() to Integer:
Integer j = (Integer)genericMethod2(list);
This cast is legal, because every Integer is also an Object, and furthermore it succeeds here because you passed in a collection of Integer. This second method is not the same scenario as the first one you highlighted for us.
When calling the method, genericMethod you are assuming that it returns array of integers, which is NOT correct. It actually returns array of type Object at runtime.
List<Integer> input = new ArrayList<Integer>();
input.add(1);
Object[] output = genericMethod(input);
for(Object obj : output){
System.out.println("Value= "+ (Integer)obj);
}
So we need to cast the individual content of the array.
One general guidline is that we shouldn't mix ARRAY and GENERICS in Java.
Update:
Reference from Effective Java:
In Summary, arrays and generics have very different type rules. Arrays are covariant and reified; generics are invariant and erased. As a consequcne, arrays provide runtime type safety but not compile-time type safety and vice versa for generics. Generally speaking, arrays and generics don’t mix well. If you find yourself mixing them and getting compile-time error or warnings, your first impulse should be to replace the arrays with lists.
Another way is to do it like in java.util.ArrayList.toArray(T[]).
You pass the type of Array to that Method, if it's big enough it will be reused, otherwise an new Array is generated.
Example:
List<Integer> intList = new ArrayList<>();
intList.add(Integer.valueOf(1));
intList.add(Integer.valueOf(2));
intList.add(Integer.valueOf(3));
Integer[] array = intList.toArray(new Integer[] {});
System.out.println(Arrays.toString(array));//Will Print [1, 2, 3]
Implementation of ArrayList.toArray(T[]) see here.
After Java 8 was released, you can leverage constructor references to return a generic array. While there are more straightforward options to convert List to Integer[], I am explaining the concept here with kinda minimal changes to your code:
I am not touching your genericMethod2() implementation
For genericMethod1, let's add a second parameter that will accept a constructor reference. Then you can call the apply() function on the constructor reference to create a generic array.
you pass Integer[]::new to this new parameter. Integer[]::new is treated as (int x) => new Integer[x]
fi.apply(input.size()) calls (int x) => new Integer[x] with argument input.size(). The result is a Integer[]. This is why the IntFunction parameter has generic type T[]
import java.util.List;
import java.util.function.IntFunction;
import java.util.LinkedList;
public class RetGenericArr {
public static <T> T[] genericMethod1(List<T> input, IntFunction<T[]> fi) {
T[] res = fi.apply(input.size());
int i = 0;
for (T t : input) {
res[i] = t;
i++;
}
return res;
}
public static <T> T genericMethod2(List<T> input) {
return input.get(0);
}
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<Integer>();
list.addFirst(1);
list.addFirst(2);
// umm I am uncomfortable about the variable naming below:
Integer[] i = genericMethod1(list, Integer[]::new); // Now no error/warning. Note that I am not casting now
for (int e: i) {
System.out.println(e);
}
Integer j = genericMethod2(list);
}
}
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.)
i created a class derived of Object and I have a object array.
how to do cast object array to my class array?
public class CastArray{
public CastArray(){
}
public long toLong(){
return Long.parseLong(this.toString());
}
public double toDouble(){
return Double.parseDouble(this.toString());
}
public int toInteger(){
return Integer.parseInt(this.toString());
}
}<br />
return Error:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [LBasic.CastArray;
The question is a bit vague but let me make an attempt to answer it. if the object array is actually an array of your class, you can do a direct cast to your array e.g. A[] a = (A[]) objArray;. Alternatively, if you know that each element can be cast into your class (is an instance of your class or one of its sub classes), you can clone it by creating a new array and adding each element with a cast to your class. e.g.:
A[] a = new A[objArray.length];
int i = 0;
for (Object o : objArray) {
a[i++] = (A) o;
}
It sounds like you're asking how to do this:
Object[] objectArray = {};
CastArray castArray = (CastArray)objectArray;
If this is the case you can't.
The Arrays.copyOf(...) could be a solution for you.
Best regards!