I am creating a stack of generic type that is backed by an array. When I try to make a generic type array, Java does not let me. I've been told that I must create an array of type Object and cast it to a generic type. I've casted my Object array to type , but how can I deal with the Unchecked Type error that Java keeps giving me?
public class AStack<T>{
// Create a stack with the default capacity 10. The stack expands
// its internal array when the number of elements pushed() into it
// would exceed the internal capacity.
Object arr[];
int top=-1;
public AStack(){
int defSize=10;
arr = (T[])new Object [defSize];
}
This is where I am so far.
UPDATE:
I am creating an Object array, then casting the return types to type T at the end of the method.
The simplest way is to use the type variable to cast array of objects to desired type.
public class AStack<T> {
T arr[];
int top=-1;
public AStack() {
int defSize=10;
arr = (T[]) new Object [defSize];
}
}
You could use an interface or a class that is extended to all the other classes you want to make generic and then use the interface or the class as the type of the array and now you could insert your specific types into the array.
GenericType[] array = { new specificType1(), new specificType2(), new specificType3() }
Related
I've got this Generic class, with a method returning a Generic Array:
public class ZTagField<T> extends JTextPane {
public ZTagField(StringBasedFactory<T> factory) {
assert (factory != null);
this.factory = factory;
init();
}
public T[] getItems() {
...
T[] arrItems = (T[]) currentItems.toArray((T[])new Object[0]);
return arrItems;
}
And another one using it:
public class Xxx {
ZTagField<clTag> txtTags = null;
public Xxx() {
txtTags = new ZTagField<clTag>(createFactory());
}
public clTag[] getSelectedTags() {
return txtTags.getItems();
}
}
This latter txtTags.getItems()gives me an exception : ==> Exception [Object cannot be cast to [clTag ????
Can anyone explain me why ?
I've trie to apply as much of this How to create a generic array, to no avail.
I've got an ugly workaround :
return Arrays.asList(txtTags.getItems()).toArray(new clTag[0])
But I'd like to have it in then ZTagFieldClass.
Works as designed: at runtime there is no generic type.
It gets erased, and an array of Object is created. An array of Object can not be cast to another kind of array.
This is one of the restrictions of Java generics, based on the way how they are implemented.
That is way you are advised to be careful using arrays together with generics. You might prefer to use a generic List instead.
And just to be clear about this: yes, you can get arrays to work with generics, as elegantly shown by the other answers. But: you spend your time fighting symptoms doing so. Arrays and generics don't go together nicely in Java. Accept that, use generic lists and thus: fix the problem instead of working around it.
Arrays are reified. That means that they hold a reference to their component type, and when inserting an element, it uses that reference to check if the inserted element is actually a subtype of the component type.
Because of this, to create a T[], you need a concrete reference to the component type T, and since generics are erased, the generic T doesn't count. (That's also why you can't straight up create a T[] like T[] arr = new T[].)
The toArray method of a collection gets around this by having the user pass an array of the component type. But you try to cheat this by casting your Object[] to a T[], which doesn't actually create an array of T (where would the reference to the concrete T come from?). Such a cast would fail if it weren't unchecked, unless T was actually Object.
That's also where the ClassCastException comes from. You create an Object[], so the component type is Object, no matter if you cast it to T[], the component type stays Object. But later on, you know the actual component type you want (clTag):
public clTag[] getSelectedTags() {
return txtTags.getItems();
}
So the compiler will insert an implicit cast here to clTag[]:
public clTag[] getSelectedTags() {
return (clTag[]) txtTags.getItems();
}
But you can not cast an Object[] to a clTag[], just like you can not cast an Object to clTag.
Your workaround works, because you're actually supplying a reference to the component type:
Arrays.asList(txtTags.getItems()).toArray(new clTag[0]) // <-- 'clTag' here
A more modern solution than passing an array of the component type is to pass a IntFuntion<T[]>, which encapsulates an array constructor, to the method:
public T[] getItems(IntFunction<T[]> arrCons) {
...
T[] arrItems = currentItems.toArray(arrCons.apply(0));
return arrItems;
}
...
txtTags.getItems(clTag[]::new);
But you can't get around having to pass the component type in some way or another, unless you switch to returning a List<T> (as GhostCat also suggested). Since generics are not reified, you can create a List<T> without a reference to a component type:
public List<T> getItems() {
...
return new ArrayList<>(currentItems);
}
After compilation, the types are erased.
Since T is not bounded to a specific type, T will be replaced by Object.
So, this :
T[] arrItems = (T[]) currentItems.toArray((T[])...);
return arrItems;
will not create and return an array of the specific type used by the instance of the class at runtime but will only create an array of Object.
Besides, in Collection.toArray() you cannot pass either an array (new T[]) because it is not valid to create a generic array.
Consequently, if you want to use the toArray() method, you can finally only pass an array of Object in this way :
Object[] arrayObject = values.toArray(new Object[currentItems.size()]);
But an array doesn't work as a List type.
An array of a specific type cannot be cast to an array of another type even if the elements that it contains are of the type of the target of the cast.
So, you cannot cast an array of Object to an array of a specific type even if the array contains elements with this specific type such as.
So this will produce a ClassCastException :
clTag[] values = (clTag[]) arrayObject;
To solve your problem :
If you can use Java 8, using a functional interface is really a clean solution.
Jorn Vernee has given a very good answer illustrating it.
Otherwise, before Java 8, the single way to create an array of the same type that the parameterized type used in a generic collection is :
1) Creating a new array with the specified type.
The java.lang.reflect.Array.newInstance(Class clazz, int length) method
allows to create an array of the specified class and length.
2) Storing the class of the declared type in the instance of the generic class. You can do it by adding a class parameter in the constructor of it.
3) Populating it from the elements of the generic collection.
An easy way is using <Object, Object> Object[] java.util.Arrays.copyOf(Object[] original, int newLength, Class<? extends Object[]> newType) method but it is not effective as first you have to convert the collection into an array with toArray() to be able to pass it to the copyOf() method.
For example with a generic List, you could write :
public class ZTagField<T> {
private class<T> clazz;
private List<T> list = new ArrayList<>();
public ZTagField (class<T> clazz){
this.clazz = clazz;
}
public T[] get() {
T[] array = (T[]) Array.newInstance(clazz, list.size());
Class<? extends Object[]> clazzArray = array.getClass();
array = (T[]) Arrays.copyOf(values.toArray(), values.size(), clazzArray);
return array;
}
}
It works but as said it is not effective.
A more effective solution would be iterating on the list and adding elements in the new array instead of using Arrays.copyOf():
public T[] get() {
T[] array = (T[]) Array.newInstance(clazz, list.size());
for (int i = 0; i < values.size(); i++) {
array[i] = values.get(i);
}
return array;
}
I have a container class that looks like this. (I'm removing all logic methods to reduce clutter)
public abstract class Container<T> {
protected T[] array;
public Container(int capacity) {
array = (T[]) new Object[capacity];
}
public T[] array() {
return array;
}
}
This class is used, like so:
public final class ItemContainer extends Container<Item> {
public ItemContainer(int capacity) {
super(capacity);
}
}
This container now, should hold an array of Items. However when trying to pass it to an Item[] to be used, the following error is presented:
Exception in thread "ForkJoinPool-2-worker-1"
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to
[Lcom.oblivion.entity.item.Item;
How can I get around this?
When you create an array of Object (or any other object in fact), it doesn't change types when you cast, in fact casting to the generic T[] has no effect at all. Therefore the array method returns an array of Object, which the caller tries to cast implicitly to an array of Item, and that of course fails.
If you want to create an array of the parametric type you should have a method that accepts a Class<T>. Then you can create the array using the Array.newInstance method.
Alternatively you can use the approach used by ArrayList: use an array of Object internally and require the caller to pass in an array of the appropriate type if they want a snapshot of the data.
public Container(Class<T> clazz, int capacity) {
array = (T[]) Array.newInstance(clazz, capacity);
}
The need for a cast is a bit of a mishap in language design. There is an overloaded newInstance with multiple dimensions, hence in the case of T[]...[]a generic result type cannot be done.
You might want to store the component class for object creation or casts:
T t = clazz.cast(object);
T t = clazz.newInstance();
I have a method (in a class which represent a set of objects) that receive as a parameter a Class object, and I need to find all the objects in the set that have this Class object as their meta-object.
The problem is that i need to return them as an array of their type, and i don't know how to get the objects type.
So in other words, i need to create an array, which his type is depends on the Class object, for example:
public Object[] getAllFromType(Class meta){
Object[] temp = new meta.getDynamicType[fullCells];
...
...
...
return temp;
}
Is it possible?
Thanks.
Well, as you've shown you're going to return it as Object[] anyway, so you won't get a compile-time benefit here, but you can create an array of the right type easily enough:
Object[] array = (Object[]) Array.newInstance(meta, fullCells);
Note that the cast to Object[] is required because Array.newInstance returns Object - the cast will fail for int[] for example, because an int[] isn't an Object[]. It will work if the array element type is any class or interface type, however.
I want to create a generic array in java maintaining the type safety usually offered by Java.
I am using this code :
class Stack<T> {
private T[] array = null;
public Stack(Class<T> tClass, int size) {
maximumSize = size;
// type unsafe
//array = (T[])new Object[maximumSize];
this.array = (T[])java.lang.reflect.Array.newInstance(tClass,maximumSize);
}
is this code type safe? ans if so, why? why if it is type safe I need a cast?
The Array.newInstance(..) method has a return type of Object. As such, you cannot directly assign it to anything other than Object. You therefore need a cast.
The method delegates to a native method which
Creates a new array with the specified component type and length
Therefore it is creating an array of type T.
The type safety, assuming array is declared as
T[] array;
, is guaranteed by the Class<T> parameter and the cast using the same type variable.
You should add the
#SuppressWarnings("unchecked")
with a comment explaining the above reason in your source code. Always comment why a cast whose warning you are suppressing is safe.
It's not type safe because of the primitive Class objects. For example I can create a new Stack in the following manner:
new Stack<Boolean>(boolean.class, 10);
Which is OK with the compiler but throws an exception because boolean.class is a Class<Boolean> and boolean[] cannot be cast to Boolean[].
The alternative you show commented out:
array = (T[])new Object[size];
Is actually somewhat type safe but for a different reason: it is due to erasure. You cannot, for example, cast a new Object[size] to a Number[], but the cast never happens on the array. It happens some time later, like when you return an element of the array from a method (in which case the element is casted). If you tried to do something like return the array to outside the object it will throw an exception.
Usually the solution is not to generically type the array. Instead, do something like this:
class Stack<E> {
Object[] array = new Object[10];
int top;
void push(E elem) {
if(top == array.length)
array = Arrays.copyOf(array, array.length * 2);
array[top++] = elem;
}
E pop() {
#SuppressWarnings("unchecked")
E elem = (E)array[--top]; // type safe cast
array[top] = null;
return elem;
}
}
The above cast is type safe because you can only push an E in to the array. Both the JDK Stack (which extends Vector) and ArrayList work this way.
If you want to use newInstance, you would have to reject primitives as there is no way to represent them generically:
Stack(Class<T> tClass, int size) {
if(tClass.isPrimitive())
throw new IllegalArgumentException();
// ...
}
Since Array.newInstance returns an Object it needs a cast. The compiler will always give warning in such cases. This is the limitation of generics.
As we know that generics are checked at the compile time so, my friend
java.lang.reflect.Array.newInstance(tClass,size); returns you the object and you are type casting it, and if array is not of type T[] then there can be compile time error
Suppose I have a generic class with a generic parameter T which is a Number subclass. I would like to initialize an array of T during class construction. Is it possible? If yes how? If not why?
public class AClass<T extends Number>{
private T array[];
private int arrayOfInt[];
public AClass(int size){
arrayOfInt = new int[size];
array = ? //what should I put here?
}
}
T is only know at compile time. It is not know at runtime and thus you cannot initilise the contents of the array. However you can create the array, every value will be null.
array = (T[]) new Number[size];
EDIT: The problem with creating instances of any type is you need to know what is the default value you want and which constructor you want to call. e.g. there is no new Double()
As mentioned below, double[] will be more efficient and faster than Number[] and unless you need large long values, it will be able to store every possible value.
If you want to use arrays, there are two options:
Peter Lawrey's answer, array = (T[]) new Number[size];. You have to make sure never to return or pass this variable to code outside of the class that expect it to be an array of a particular type, which will cause an exception.
Declare array as type Number[], then just do array = new Number[size];. The downside of this is that when you get anything out of it you will need to explicitly cast to T to use it as such.
The two are the same after type erasure, and they will both cause unchecked cast warning, so it's really a matter of personal preference. The former is more convenient, while the latter is more formally correct (you are not pretending it's a type it's not).
Alternately, some people will tell you to use an ArrayList<T> instead. But internally, an ArrayList is still implemented using one of these two options.
This is not possible.
Because Java generics use type erasure, the type of T isn't known at runtime, so you can't create an array of it.
Other options than mentioned are to use toArray(T[]) or java.lang.reflect.Array:
public class AClass<T extends Number>{
private T array[];
public AClass(final int size, T[] a){
array = (new ArrayList<T>() {{
for (int i = 0; i < size; i++) {
add(null);
}
}}).toArray(a);
}
public AClass(int size, Class<T[ ]> clazz) {
array = clazz.cast(java.lang.reflect.Array.newInstance(
clazz.getComponentType( ), size));
}
public static void main(String[] args) {
System.out.println("toArray: "
+ new AClass<Double>(42, new Double[]{}).array.length);
System.out.println("java.lang.reflect.Array: "
+ new AClass<Double>(42, Double[].class).array.length);
}
}
PS. solution using reflection is close to one suggested in Langer's Generics FAQ (Utilities.createBuffer): How do I generically create objects and arrays?