This is the code: A simple ceneric class and trying to assign an integer to aa[0].
public class GenericTest<T> {
T [] aa = (T[]) new Object[2];
T bb;
public GenericTest(T x, T y) {
aa[0] = x; aa[1] = y;
System.out.println(aa[0] + " " + aa[1]); //OK
}
static public void main(String[] args) {
GenericTest<Integer> ll = new GenericTest<>(1,2);
ll.bb = 1; // OK
ll.aa[0] = 6; // ClassCastException from Object to Integer
}
}
In fact, the exception message is this:
java.lang.ClassCastException:
[Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
It is saying that it can't cast an Object[] to an Integer[].
The root cause of is the initializer in:
T [] aa = (T[]) new Object[2];
That typecast is an unsafe typecast. And indeed the compiler tells you that something is wrong:
$ javac GenericTest.java
Note: GenericTest.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Anyhow ... what is happening is that when you then do this:
ll.aa[0] = 6;
the JVM is trying to cast the ll.aa to an Integer[] ... because that is what the static typing says that it should be. But it isn't an Integer[]. It is an Object[]. Since Object[] is not assignment compatible with an Integer[] that gives you a class cast exception.
(Why is it doing a hidden type cast? Well this is how the JVM ensures runtime type safety in the face of possible unsafe casts and the like!)
How to fix it?
Avoid using T[]. Use List<T> instead.
Unfortunately, if you have to use T[] there is no easy fix. Basically arrays of a generic type parameter are difficult to create. You end up having to pass the Class object for the parameter's actual class as an extra parameter. Something like this:
import java.lang.reflect.Array;
public class GenericTest<T> {
T [] aa;
T bb;
public GenericTest(Class<T> cls, T x, T y) {sy
aa = (T[]) Array.newInstance(cls, 2);
aa[0] = x; aa[1] = y;
System.out.println(aa[0] + " " + aa[1]); //OK
}
static public void main(String[] args) {
GenericTest<Integer> ll = new GenericTest<>(Integer.class, 1, 2);
ll.bb = 1; // OK
ll.aa[0] = 6; // ClassCastException from Object to Integer
}
}
There is still a warning about an unsafe typecast ... but in this case it is safe to suppress the warning.
For Java 8 onwards, there is another solution which involves passing a reference to the array constructor for Integer[]; see Andy Turner's answer. This is cleaner than using reflection and calling Array.newInstance, but you still have to pass an extra parameter to the constructor.
This is what happens when you use generics. Because generics are erased at runtime, compiler still needs to somehow be safe (after erasure) that things work correctly. Let's simplify this:
GenericTest<Integer> ll = new GenericTest<>(1,2);
ll.bb = 1; // OK
System.out.println(ll.aa.getClass());
The last line is going to be translated to:
28: getfield #7 // Field aa:[Ljava/lang/Object;
31: checkcast #42 // class "[Ljava/lang/Integer;"
notice the checkcast. Since your T was resolved as Integer, means that the array must be Integer[] too; when in reality it is Object []. Compiler is trying to warn you btw when you do :
T [] aa = (T []) new Object[2];
because this is unsafe. In general, generic arrays are a major headache in java, imo.
This line:
ll.aa[0] = 6;
fails because the compiler has inserted some casts. Because it knows that ll is a GenericTest<Integer>, it expected aa to be an Integer[].
The compiler inserts a cast to this type before you can do anything with it: it is evaluated identically to:
((Integer[]) ll.aa)[0] = Integer.valueOf(6);
The problem is that ll.a isn't an Integer[], it's an Object[], so this cast fails.
These casts are inserted even when you're doing something that could be applied to "any array", or even "any object": for example, ll.a.toString() would have casts inserted, even though toString is available on all subclasses of Object, i.e. all objects.
T [] aa = (T[]) new Object[2];
As already pointed out by StephenC, this is an unchecked cast. Checked casts are instructions inserted into the bytecode, which statically check that the object is of a particular type. But, because T is a type variable, there is no "particular type" that the compiler can choose to insert a checkcast instruction on that assignment.
The correct way to deal with this is either:
Use a List<T> instead of a T[] as the field type. Generics and arrays don't play nicely together anyway, so you're better off sticking with generics.
You can use a fixed-length array, e.g. Arrays.asList(null, null), to mimick an array, insofar as you can only set the elements, not clear/add/remove etc.
Inject a T[] (or a Supplier<T[]>, or an IntFunction<T[]> etc) into the constructor of the GenericTest, in order that you push the burden of ensuring the correct type onto the caller:
public GenericTest(T x, T y, IntFunction<T[]> arrayFn) {
// ...
aa = arrayFn.apply(2); // Creates an array of the right length.
}
// Then...
GenericTest<Integer> ll = new GenericTest(1, 2, Integer[]::new);
ll.aa[0] = 6;
This will then be type-correct, because aa was created using Integer[]::new, i.e. it's an Integer[]. The casts are still inserted, they just pass the type check.
T [] aa = (T[]) new Object[2];
Because that line is broken. Your compiler warned you when you tried to compile it. As a general rule, if you get a compiler warning you do not understand, then do something else - java compiler warnings are best treated as: "Your code is completely broken and this is not going to work, but for the benefit of e.g. trying to start this application up because you want to test a completely different part of it, we'll just slap this broken stuff in a class file so you can move on, for now. Do not under any circumstances publish this to production until you fully understand this warning first."
In java, generics is a 100% compile-time show: Generics are compiler-checked documentation. At runtime, generics are mostly erased, and the few places where they remain, they are 'comments' as far as the runtime is concerned. The verifier and the runtime system never does any checks, ever. If javac allows it, the generics are now done being useful.
This in sharp contrast to arrays: Arrays are their own object and they DO know their 'component type'. Also, the compiler doesn't do all that much checking on them, but the runtime DOES. If you use some classfile hackery to put an Integer in a List<String>, the runtime will let you. However, you cannot use any amount of hackery to put an Integer in a String[]. It just won't work - you end up with an ArrayStoreException instead.
Because arrays actually know their type, at runtime, Integer[] and Object[] are different types (whereas at runtime, a List is just a List - the runtime doesn't know what generics are).
T is Integer, and that variable is of type T[], therefore, it is Integer[], therefore, any interactions with aa get you an implicit cast to Integer[]. This is 100% analogous to this code:
List<String> list = new ArrayList<String>();
List raw = list; // legal, but gets you a warning.
raw.add(5);
System.out.println(list.get(0));
You can compile it. If you run it, the last line throws ClassCastException which is weird, there is no cast there at all! Ah, but there is. Because the compiler rewrote that for you, into this code (and remember, the runtime does not know generics, which is why it is rewritten like this):
List list = new ArrayList();
List raw = list;
raw.add(Integer.valueOf(5));
String $v = (String) list.get(0);
System.out.println($v);
You'd think: Wait, why cast it? There is a System.out.println(Object) variant? But, that's just the rules. Java will immediately cast that because it can, after all, list is a List<String> which means its get(int idx) method can be assumed to always return strings, so java will cast it as soon as possible even if not needed.
The exact same thing happens in your code: It sees T[], where T is bound to Integer, so, aa is cast to Integer[] even if it wasn't needed. This cast fails at runtime because an object created as new Object[2] is clearly not an Integer[]. After all, I can invoke:
Integer[] x = ...;
System.out.println(x.getClass().getComponentType());
and this code is guaranteed to print java.lang.Integer. If I can write code so that java.lang.Object is printed, that would be broken, and yet that is exactly what would happen.
So how do I fix it?
Do not, ever, write T[]. It's as simple as that.
Arrays are low-level constructs you shouldn't be using, especially if the component type is non-primitive. If you MUST use them, then they should be relegated to internal implementation details, hidden behind a nicer API.
Do the casting in those wrappers.
This is precisely how ArrayList works. ArrayList has an array inside that holds your list elements (hence the name ArrayList). That is defined as Object[] storage = new Object[...];. Not T[] storage = (T[]) new Object[..]. Then, the get method of ArrayList does the casting:
public T get(int idx) {
// check if idx is within 0-size()...
return (T) storage[idx];
}
This cast does nothing (javac emits a warning that it has no idea what T might be and the runtime doesn't either, so nobody can check this), but this will all work at runtime (obviously: ArrayList works fine).
You need to do the same thing, and hide that array, if you must use it. Or better yet, don't do any of this stuff and use List<T> instead. Arrays of non-primitive types are a blight: If you use them, you better have an excellent reason for it, and hide it as much as possible.
This code works (printing the class inside the constructor....) and I don't know why
public class GenericTest<AnyType> {
public AnyType [] aa = (AnyType []) new Object[2];
public AnyType bb;
public GenericTest(AnyType x, AnyType y) {
aa[0]=x; aa[1]=y; System.out.println(aa.getClass());
System.out.println( aa[0]+" "+aa[1]); //OK
}
static public void main(String[] args) {
GenericTest<Integer> ll = new GenericTest<>(1,2);
ll.aa[0]= 6; // ClassCastException from Object to Integer
}
}
Assuming that I have the following class
public class A <T>{
private T [] datas;
// more code here ...
}
And I desire to take advantage of the constructor to initialize the array. Suppose that I have the following constructor
public A(T element){....}
Java does not allow me to use something like
datas = new T[10]
And it will complain that I cannot create a generic array of T
But I can still use a work around like:
#SuppressWarnings("unchecked")
public A(T element){
List<T> datasList = new ArrayList<T>();
datasList.add(element);
datas =(T[]) datasList.toArray();
}
I have a warning from the compiler that's why I had to add the #SuppressWarnings, but my point is related to the following comment from the toArray method documentation (Please take a look at the picture)
It talks about the returned array being safe. So does that means it is safe to use this method? If not why? And what would be a better way to do such an initialisation in a constructor? I would like to also consider the case of a variable list of T elements in an overloaded constructor like
public A(T... elements){....}.
You can create an instance of a generic array using the following:
public A(T element){
int length = 10;
datas = (T[])Array.newInstance(element.getClass(), length);
}
However, there's a problem if element would be a subclass of T, e.g. if you'd call it like this:
A<Number> numberA = new A<>( Integer.valueOf(1) );
Here T would be Number but the class of element would be Integer.
To mitigate that you could pass a vararg array of type T, e.g. like this:
//firstElement only exists to force the caller to provide at least one element
//if you don't want this then just use the varargs array
A(T firstElement, T... furtherElements){
int length = 10;
Class<?> elementClass = furtherElements.getClass().getComponentType();
datas = (T[])Array.newInstance( elementClass, length);
}
Since varargs always result in an array (even of length 0) you'll get an array of type T and can get the component type of that.
So in the case above numberA.datas would be a Number[] array and not an Integer[] array.
You can pass generics, but you can't call new T (or new T[ ]).
Keep in mind that generics are gone after compilation, so it actually only helps when writing the code. Knowing it's gone during runtime, it's also obvious that new T( ) can't be called as generic, T is removed in runtime.
It's safe to do, because you create that list in full control, accepting only objects of your generic type.
A nicer way (imho) is to create a static method as it is purely input-->output. You have to declare your generics before the method return type:
public < T > T[ ] toArray(T... objects) { ... }
I have a class MyStack<T> which defines the following
public T[] toArray(){
int s=size();
#SuppressWarnings("unchecked")
T[] result=(T[])new Object[s];
Node n=first;
for (int i=0; i<s; i++){
result[i]=n.data;
n=n.next;
}
return result;
}
Since this returns an array of type T, I would think that if I declared this instance: MyStack<String> s=new MyStack<>, that the following would be perfectly valid: String[] test=s.toArray(). I think this because since s is of type String, toArray should return an array of type String, since String has basically been substituted in for every T in this class (only for this particular instantiation, I know). The only way this runs without errors is if I do this: Object[] test=s.toArray().
Why is this?
In a word, type erasure. Taken from the Java website:
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.
What this means is that, when your code is compiled, MyStack<String> is compiled into MyStack<Object>. This is to make sure that generics do not incur an overhead by needing to create new classes. How does this apply to you? Well..
MyStack<String> s = new MyStack<>();
is converted into..
MyStack<Object> s = new MyStack<>();
Now, this means that when you call the toArray method, the only type that can be guarenteed is the Object type. The compiler can't be sure that everything it returns is of type String, so it won't let you treat it as a String, due to the strong typing in Java. So, what is the only variable type left?
Object[] array = s.toArray();
Extra Reading
Type Erasure in Java.
Well, hold on a minute. Suppose your hypothesis were correct that String were substituted for every T.
Would the following cast be valid?
String[] result = (String[])new Object[s];
No, it would not. We can be sure that a new Object[] is not a String[].
Now sometimes you will see something like (T[])new Object[n] but it only works because the cast actually becomes erased inside the generic class. (It is a deceptive idiom.)
When the class gets compiled, what actually happens is that references to T are replaced with its upper bound (probably Object unless you had something like <T extends ...>):
public Object[] toArray(){
int s=size();
Object[] result=new Object[s];
Node n=first;
for (int i=0; i<s; i++){
result[i]=n.data;
n=n.next;
}
return result;
}
And the cast is moved to the call site:
MyStack stack = new MyStack();
String[] arr = (String[])stack.toArray();
So in fact, while the cast is erased inside the class, the cast does happen once the value is returned to outside the class, where ClassCastException is thrown.
The inability to instantiate arrays (and objects in general) generically is why the Collections framework defines their toArray method to take the return array as an argument. A simple version of this for you would be like the following:
public T[] toArray(T[] inArray){
int s = size();
Node n = first;
for (int i = 0; i < s; i++){
inArray[i] = n.data;
n = n.next;
}
return inArray;
}
For some ideas on how to create an array generically, you may see 'How to create a generic array in Java?'; however you will need the caller to pass some argument to the method.
This example is taken from Thinking in Java.
public class Automobile {
}
public class Holder<T> {
private T a;
public Holder(T a){
this.a = a;
}
public void set(T a){
this.a = a;
}
public T get(){
return a;
}
public static void main(String[] args){
Holder<Automobile> h = new Holder<Automobile>(new Automobile());
Automobile a = (Automobile)h.get();
}
}
Then there goes explanation: you must specify what type you want to put into it using the same angle brackets syntax as you can see in main().
Well, I can't understand anything. I would understand that must word as a possible compile time error in case of violation of this rule.
But this works:
Holder<Automobile> h = new Holder(new Automobile());
Automobile a = h.get();
And this works:
Holder h = new Holder(new Automobile());
Automobile a = (Automobile)h.get();
So, as I can see, the compiler won't control what I put into the Holder object. Well, then I don't catch generics at all. And I have two questions:
What is the reason to use them? Only to save me some effort when casting object back to Automobile?
Is there any way to make the compiler control me so that I should really put Automobile into the Holder?
The casting here is unnecessary:
Holder<Automobile> h = new Holder<Automobile>(new Automobile());
Automobile a = (Automobile)h.get();
But here it is necessary:
Holder h = new Holder(new Automobile());
Automobile a = (Automobile)h.get();
And this is the best way of doing things ever since java 1.5 and above:
Holder<Automobile> h = new Holder<Automobile>(new Automobile()); //specify the generic parameter on both static and dynamic type
Automobile a = h.get(); //no casting is necessary
Or above java 1.7 for simplicity:
Holder<Automobile> h = new Holder<>(new Automobile()); //diamond operator so you don't need to respecify the same thing
Automobile a = h.get();
The reason why it's useful to use generics in this fashion is so that you can't do the following:
Integer a = new Integer(6);
List list = new ArrayList();
list.add(a);
list.add("5");
for(int i = 0; i < list.size(); i++)
{
Integer integer = (Integer)list.get(i); //crashes at "5" which is String at runtime
System.out.println(integer);
}
As you can see, if you can put any subclass of Object into the list without bounds, then you need explicit casting, and if you put anything that is not what you expect it to be into the List, then it will crash. Please note that without generics, you're also not told what type you are expected to place into the list, which means you need to keep track of what the List is supposed to contain, which is really crappy when you're trying to do logic like the following: Java generics and casting to a primitive type
And I'm not even sure if this is possible without generics: Is it possible to cast Map<Field, Value> to Map<Mirror, Mirror> when it is known that Field and Value extend Mirror?
So technically generics enable extra features while also encourages type safety and therefore error-less code, which is always nice.
Integer a = new Integer(6);
List<Integer> list = new ArrayList<Integer>();
list.add(a);
list.add("5"); //this will not compile -> you won't need to wait until runtime to see that things are incorrect!
The point of generics is to write datastructures which can contain or work with any object. You specify at use-time which types of objects it should contain, e.g.:
ArrayList<String> stringList = new ArrayList<String>();
(Diamondoperator for show not written in short)
to answer the questions:
1: to write code which can work with all objects (like the collections), and yes, the compiler understands and can type-check your code at compiletime
2: yeah, dont use generics and instead write the code spezific for your automobiles
Here is my code:
Object[] data = GeneComparison.readData(files);
MyGenome genome = (MyGenome) data[0];
LinkedList<Species> breeds = (LinkedList<Species>) data[1];
It gives this warning for the LinkedList:
Type safety: Unchecked cast from Object to LinkedList<Species>
Why does it complain about the linked list and not MyGenome?
Java complains like that when you cast a non-parameterized type (Object) to a parameterized type (LinkedList). It's to tell you that it could be anything. It's really no different to the first cast except the first will generate a ClassCastException if it is not that type but the second won't.
It all comes down to type erasure. A LinkedList at runtime is really just a LinkedList. You can put anything in it and it won't generate a ClassCastException like the first example.
Often to get rid of this warning you have to do something like:
#SuppressWarning("unchecked")
public List<Something> getAll() {
return getSqlMapClient.queryForList("queryname");
}
where queryForList() returns a List (non-parameterized) where you know the contents will be of class Something.
The other aspect to this is that arrays in Java are covariant, meaning they retain runtime type information. For example:
Integer ints[] = new Integer[10];
Object objs[] = ints;
objs[3] = "hello";
will throw a exception. But:
List<Integer> ints = new ArrayList<Integer>(10);
List<Object> objs = (List<Object>)ints;
objs.add("hello");
is perfectly legal.
Because here:
MyGenome genome = (MyGenome) data[0];
You are not using generics
And here
LinkedList<Species> breeds = (LinkedList<Species>) data[1];
You are using them.
That's just a warning, you are mixing types in the data array. If you know what are you doing ( I mean, if the second element do contains a LinkedList ) you can ignore the warning.
But better would be to have an object like this:
class Anything {
private Object [] data;
public Anything( Object [] data ) {
this.data = data;
}
public Gnome getGnome() {
.....
}
public List<Species> getBreeds() {
......
}
}
And have to methods returning proper things, prior to a correct conversion so you end up with:
Anything anything = new Anything( GeneComparison.readData(files) );
MyGenome genome = anything.getGnome(); // similar to data[0]
LinkedList<Species> breeds = anything.getBreeds(); // similar to data[1];
Inside those methods you have to do proper transformations.