Creating an object variable out of its meta-object - java

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.

Related

Generic array cast exception

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;
}

How to return a generic array if I can't pass in the class type?

So I understand that generics and arrays really don't mix well, but I have an assignment where I have to make it work. I need to return the underlying array of a set ADT using the toArray() method, but I'm not allowed to pass in something like Class c that I could use to return the generic array as a specific class type.
So the following would work, but is not allowed:
public T[] toArray(Class<T> c){
#SuppressWarnings("unchecked")
T[] returnContents = (T[]) Array.newInstance(c, size);
for(int i = 0; i<size; i++){
returnContents[i] = contents[i];
}
return returnContents;
}
I can't pass in the class type because the Set class I'm in implements a SetInterface that doesn't have a parameter for the toArray method. Thus I'm being limited to what I can do.
public T[] toArray(){
#SuppressWarnings("unchecked")
T[] returnContents = (T[]) new Object[size];
for(int i = 0; i<size; i++){
returnContents[i] = contents[i];
}
return returnContents;
}
Above is what I have now, which will return an array of type Objects because (if I understand correctly) type erasure don't carry through after compiling.
In my client code, I have the following line that will give me a casting exception
ProfileInterface [] friends = new ProfileInterface[friendsList.getCurrentSize()];
friends = Arrays.copyOf(friendsList.toArray(), friendsList.toArray().length);
friendsList is a generic Set
Any ideas on a work around for this?
Your option is to send back a Object[] array in the toArray() method and then use Arrays.copyOf(U[], int, Class) to convert.
So, your code will be
ProfileInterface [] friends = Arrays.copyOf(friendsList.toArray(), friendsList.toArray().length, ProfileInterface[].class);
And toArray() method can just be
public Object[] toArray(){...}
Here, even if your toArray() method return T[] it will still work fine, because T will be inferred as Object.
If anyone runs into this problem in the future: coughs CS445 Assignment 1
The easiest way to accomplish this without breaking the generic nature of the set class is the following...
As discussed, we need to return the following (or a copy of it in this case)
T[] returnContents = (T[]) new Object[size];
Now, since Java will read the (T[]) at compile time, this is fine - but at run time we can't have a T array, so we use Object.
The problem is you can't cast an Object Array to a ProfileInterface Array in your client code (or any non-generic class for that matter).
So the only solution in a limited set of actions is to deal with the fact that the client can assume that all of the "Objects" in the array will be an Object that implements ProfileInterface (so Profile / ProfileInterface)
So in your client code, you will create a new Object array and iterate through it, casting and setting each individual item in the array as a Profile.
Object[] all = new Object[yourSize];
ProfileInterface [] friends = new ProfileInterface[yourSize];
all = Arrays.copyOf(friendsList.toArray(), friendsList.toArray().length);
for(int i = 0; i < all.length; i ++){
friends[i] = (ProfileInterface) all[i];
}
This will allow you to return the generic toArray to your client and then you can manipulate it.
PLEASE NOTE: This only works in this specific case where you KNOW FOR
CERTAIN that no other object type will be returned by your generic
class. If you can pass in the class type as a parameter, you very
simply fix this problem in the Generic class.

Casting Object array to generic type

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() }

Java generics, Casting a T[] to original type

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();

Create array of objects at run time using class name in java

We all know we can create an object with the help of class name in the string format. Like i have a class name "Test". Using
Class.forName("Test").newInstance()
We can create the object of that class.
My question is that is there any way to create an array or array list of the objects using class name ?? OR lets suppose we have an object of the class and can with this object we create the array or array list of the that object.
To create an array, you can use java.lang.reflect.Array and its newInstance method:
Object array = Array.newInstance(componentType, length);
Note that the return type is just Object because there's no way of expressing that it returns an array of the right type, other than by making it a generic method... which typically you don't want it to be. (You certainly don't in your case.) Even then it wouldn't cope if you passed in int.class.
Sample code:
import java.lang.reflect.*;
public class Test {
public static void main(String[] args) throws Exception {
Object array = Array.newInstance(String.class, 10);
// This would fail if it weren't really a string array
String[] afterCasting = (String[]) array;
System.out.println(afterCasting.length);
}
}
For ArrayList, there's no such concept really - type erasure means that an ArrayList doesn't really know its component type, so you can create any ArrayList. For example:
Object objectList = new ArrayList<Object>();
Object stringList = new ArrayList<String>();
After creation, those two objects are indistinguishable in terms of their types.
You can use Array
Object xyz = Array.newInstance(Class.forName(className), 10);
It has a method newInstance(Class, int):
Creates a new array with the specified component type and length. Invoking this method is equivalent to creating an array as follows:
int[] x = {length};
Array.newInstance(componentType, x);

Categories

Resources