Reputation: 13777
I am trying to write some simple numerical code in Java where one can choose between a float and double later. A simplified version of my class looks like the example below:
public class UniformGrid<T> {
public T[] data;
public UniformGrid(int arrayDim) {
data = new T[arrayDim];
}
}
This didn't work I got a generic array creation
error when trying to compile. Googling and reading some SO answers I learned about java.lang.reflect.Array
and tried to use
data = (T[]) Array.newInstance(T.class, arrayDim);
Which also didn't work, since T is (probably) a primitive type. My Java knowledge is quite rusty (especially when it comes to generics) and I would like to know why the new operator cannot be used with a generic array type. Also of course I am interested in how one would solve this problem in Java.
Upvotes: 1
Views: 1518
Reputation: 27153
This is possible, as long as you use Float
and Double
instead of float
and double
, as primitive types are not allowed in Java Generics. Of course, this will probably be quite slow. And, you won't be able to (safely) allow direct public access to the array. So this answer is not very useful, but it might be theoretically interesting. Anyway, how to construct the array ...
data = (T[]) new Object[arrayDim];
This will give you a warning, but it's not directly anything to worry about. It works in this particular form - it's inside a generic constructor and data
is the only reference to this newly constructed object. See this page about this.
You will not be able to access this array object publicly in the way you might like. You'll need to set up methods in UniformGrid<T>
to get and set objects. This way, the compiler will ensure type-safety and the runtime won't give you any problems.
private T[] data;
public void set(int pos, T t) {
data[pos] = t;
}
public T get(int pos) {
return data[pos];
}
In this case, the interface to set
will (at compile-time) enforce the correct type is passed. The underlying array is of type Object[]
but that's OK as it can take any reference type - and all generic types are effectively List<Object>
or something like that at runtime anyway.
The interesting bit is the getter. The compiler 'knows' that the type of data
is T[]
and hence the getter will compile cleanly and promises to return a T
. So as long as you keep the data
private and only access it through get
and set
then everything will be fine.
Some example code is on ideone.
public static void main(String[] args) {
UniformGrid<A> uf = new UniformGrid<A>(1);
//uf.insert(0, new Object()); // compile error
uf.insert(0, new A());
uf.insert(0, new B());
Object o1= uf.get(0);
A o2= uf.get(0);
// B o2= uf.get(0); // compiler error
System.out.println(o1);
System.out.println(o2);
System.out.println("OK so far");
// A via_array1 = uf.data[0]; // Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [LA;
}
As you would desire, there are compilation errors with uf.insert(0, new Object())
and B o2= uf.get(0);
But you shouldn't make the data
member public. If you did, you could write and compile A via_array1 = uf.data[0];
. That line looks like it should be OK, but you get a runtime exception: Ljava.lang.Object; cannot be cast to [LA;
.
In short, the get
and set
interface provide a safe interface. But if you go to this much trouble to use an array, you should just use an ArrayList<T>
instead. Moral of the story: in any language (Java or C++), with generics or without generics, just say no to arrays. :-)
Upvotes: 1
Reputation: 7322
Item 25 in Effective Java, 2nd Edition talks about this problem:
Arrays are covariant and reified; generics are invariant and erased. As a consequence, arrays provide run-time type safety but not compile-time type safety and vice versa for generics. Generally speaking arrays and generics don't mix well.
Upvotes: 0
Reputation: 70584
Sorry, you'll have to take another approach:
If you really need this, I'd look into code generation, perhaps as part of an automated build. (A simple search & replace on the source ought to be able to turn a library operating on double into a library operating on float.)
Upvotes: 1
Reputation: 44808
You cannot create a generic array in Java because of type erasure. The easiest way to get around this would be to use a a List<T>
. But if you must use an array, you can use an Object[]
for your array and ensure that only T
objects are put into it. (This is the strategy ArrayList
takes.)
Ex:
private Object[] data = new Object[10];
private int size = 0;
public void add(T obj) {
data[size++] = obj;
}
public T get(int i){
return (T) data[i];
}
Of course you'll get an unchecked warning from your compiler, but you can suppress that.
Upvotes: 8
Reputation: 41510
Generics can't be used when creating an array because you don't know at runtime what type T is. This is called type erasure.
The solution is simple: use List<T> data
.
Upvotes: 1