for performance reasons I need to use arrays to store data. I implemented this in a generic fashion like this (see this answer):
import java.lang.reflect.Array;
public class SimpleArray<T> {
private T[] data;
#SuppressWarnings("unchecked")
public SimpleArray(Class<T> cls, int size) {
this.data = (T[]) Array.newInstance(cls, size);
}
public T get(int i) {
return data[i];
}
}
The problem is that I need the involved Class<?>es. However, I might have a more complex class hierarchy containing generics:
public class Outer<T> {
public class Inner {
}
}
I would like to initialize the array as I would with an ordinary class:
SimpleArray<Integer> intArray = new SimpleArray<>(Integer.class, 10);
intArray.get(0);
SimpleArray<Outer<Integer>> outerArray;
// how to initialize this?
SimpleArray<Outer<String>.Inner> innerArray;
// how to initialize this?
I read the post on how to (not) get the Class of something generic (here) but the bottom-line seems to be that everything is type-safety related syntactic sugar.
My question is the following: How can I create instances of the SimpleArray classes above while avoiding as much ugliness as possible?
There are two issues here.
Do you really need to pass in a Class? In this case, no. Your class does not actually need to know the element type at runtime to do its job. For example, you can just do:
public class SimpleArray<T> {
private Object[] data;
public SimpleArray(int size) {
this.data = new Object[size];
}
#SuppressWarnings("unchecked")
public T get(int i) {
return (T)data[i];
}
}
If you really needed a Class<T>, how would you get one? Well, first you need to ask yourself, what are you going to use this for? There will never be a "true" Class<T> for a non-reifiable type T because with a Class<T> you can do things like .isInstance() to check whether something is an instance of T at runtime; but of course it's not possible to check instance-of with non-reifiable types at runtime.
In this case, you're only going to pass it to Array.newInstance(), and Array.newInstance() uses the raw type anyway (it does not care about the compile-time type of the Class parameter -- the parameter type is Class<?> -- it only uses the runtime value of the Class object), it is sufficient to simply coerce a Class object representing the raw type to the appropriately-parameterized Class type:
(Class<Outer<Integer>>)(Class<?>)Outer.class
You seem to be trying to make a class that wraps an array and provides a method to get elements. The class Arrays.ArrayList does exactly that already, so there is no need to reinvent the wheel. It works as follows:
List<String> list = Arrays.asList(new String[30]);
list.set(3, "foo");
System.out.println(list.get(3));
You can't use Arrays.asList to produce a List<T> if the type T is generic without suppressing a warning because it is not possible to create a generic array. You can write a helper method to do this for you though.
#SuppressWarnings("unchecked")
public static <T> List<T> newArray(int size) {
return (List<T>) Arrays.asList(new Object[size]);
}
You can use the returned List to get and set elements without having to cast, even if the type T is generic. For example:
List<List<String>> list = newArray(30);
list.set(4, Arrays.asList("A", "B", "C"));
System.out.println(list.get(4));
Related
I want to create a helper method which gets Collection type parameter to return a list. This is what I have now:
public class Helper {
public static <T> T[] CollectionToArray(Collection<T> collection) {
return collection.stream().toArray(Object[]::new); // Error
}
public static <T> T[] ListToArray(List<T> list) {
return list.stream().toArray(Object[]::new); // Error
}
}
public class IamSoNoob {
public void PleaseHelpMe() {
List<String> list = new ArrayList<>();
Set<String> set = new HashSet<>();
String[] arrayFromList = Helper.CollectionToArray(list); // Error
String[] arrayFromSet = Helper.CollectionToArray(set); // Error
String[] array = Helper.ListToArray(list); // Error
}
}
My questions are:
Is it possible to complete CollectionToArray(Collection<T>)?
If so, how?
Also, is it possible to pass List and Set as a parameter in the first place?
Is it possible to complete ListToArray(List<T> list)?
If so, how?
But here are some restrictions due to my personal taste.
I don't want to use #SuppressWarnings
I really want to keep the part .stream().toArray(Object[]::new) (Java 8 part!)
And I have a feeling that I need to fix the part Object[]::new by using something like: <T extends Object> or <? extends T> but I can't really figure out.
Please help me out, and please provide an explanation as well, I am often confused by Generic and ?.
No, you absolutely cannot do it, if it were possible the library method Collection.toArray() would've given you the same type as your LHS but instead when you want the exact type as your LHS you have to use Collection.toArray(T[]) (even that comes with ArrayStoreExceptions i.e it is up to the programmer to provide the right type for the array), the reason being that in your toArray() you've specified Object[] to be your array and later you cannot cast it to any other type or else it will result in a ClassCastException.
The reason for all this hullabaloo is because of the way generics works i.e its a compile time thing and at runtime Java erases all type parameters to their upper bound types and hence losing type information which is required for creating arrays.
One safe way of doing it is by adding another paramter to you helper method as
public static <T> T[] CollectionToArray(Collection<T> collection, T[] arr) {
return collection.stream().toArray(value ->Arrays.copyOf(arr,collection.size()));
}
and using it as
String[] arrayFromList = Helper.CollectionToArray(list, new String[0]);
but then everybody's better off using
Collection#toArray(T[]).
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 was writing a program the other day that required me to: get the frequency of a particular object inside an ArrayList<String>, remove all occurrences of a given item, etc., etc. not specified by the List interface. I decided to write my own helper class and wanted to make it as reusable as possible. I decided to specify List as the parameter type of the collection so I could use it for any class implementing the List interface. But these classes are usually defined using generics, and I did not know what class type the item to be removed would be. So I either had to define the static helper methods generically since a static class can not contain generic types explicitly, or define the class type of the object to be removed as Object. I implemented it in both ways, see below, but am wondering if there any benefits to using one over the other.
Some further questions on the topic:
Why am I able to work around the reference of a generic type in a static context by defining it in the method header rather than the class header?
When using this static method, why do I not have to declare the class Type in its usage? i.e. ListTools_V2.getFrequencyOf(ArrayList<String> items, String s) still works.
Implementation using Object class type
import java.util.List;
/** General utility class for performing frequently needed operations
on any class implementing the List interface **/
public class ListTools {
public static void removeAllOccurrences(List items, Object o) {
while(items.contains(o)) {
items.remove(o);
}
}
public static int getFrequencyOf(List items, Object o) {
int frequency = 0;
for(Object item : items) {
if(item.equals(o)) {
frequency++;
}
}
return frequency;
}
}
Implementation using generics
import java.util.List;
/** General utility class for performing frequently needed operations
on any class implementing the List interface. This implementation
uses generics to maximize reusability. **/
public class ListTools_V2 {
public static <E> void removeAllOccurrences(List<E> items, E o) {
while(items.contains(o)) {
items.remove(o);
}
}
public static <E> int getFrequencyOf(List<E> items,E o) {
int frequency = 0;
for(E item : items) {
if(item.equals(o)) {
frequency++;
}
}
return frequency;
}
}
Both operations operate on equality (.equals()) between the given object reference and the elements inside the list, and equality is not limited to objects of the same type, so you shouldn't restrict o to be the same type as the type parameter of the list.
However, raw types are bad, so you shouldn't use the raw type List. You should parameterize it with a wildcard when there is no need to constrain the type variable against anything:
public static void removeAllOccurrences(List<?> items, Object o)
public static int getFrequencyOf(List<?> items, Object o)
Generic is better as it detects type related issues at compile time.
During runtime "erasure" takes place and there is no "meaning" to generics.
For example:
List apples = new ArrayList();
apples.add(new Apple());
apples.add(new Mango()); //compiles, but this is wrong
apples.add(new Chair()); //compiles, but this is wrong
You must of course be aware of all kinds of "pitfalls" of the generic mechanism.
Examples I can easily think of are:
List<Mango> does not extend List<Fruit> , so you should use List<? super Fruit> or List<? extends Fruit>
You cannot do something like :
T.getClassName()
Generics is the recommended way of implementing such classes. They can be used to give some implicit information to your user (whoever uses this class).
For example:
List<Cat> cats = ...;
ListTools.removeAllOccurrences(cats, new Dog());
it is meaningless since, there cannot be any dogs inside a List of Cats. But user can execute this code and imagine that he has deleted some dogs from the List of cats.On the other hand generic version informs the user about this inappropriate call.
List<Cat> cats = ...;
ListTools_V2.removeAllOccurrences(cats, new Dog()); // Compile time error because it is pointless
Additionally there is a performance gain as well. If the List of Cats contains thousands of Cats, do you really want to search for that Dog inside a large number of Cat List? (contains method actually search the entire List) or do you simply want to avoid it at the compile time? ;)
your getFrequencyOf() method body is essentially same in both the cases except in one case you are passing raw List and in another you are parameterizing it to accept any type.
1-
public static int getFrequencyOf(List items, Object o) {
int frequency = 0;
for(Object item : items) {
if(item.equals(o)) {
frequency++;
}
}
return frequency;
}
Here you List is a raw type. References to generic type List should be parameterized which, is not. But it is open for any type of List (Integer, Long, String etc.)
2-
public static <E> int getFrequencyOf(List<E> items,E o) {
int frequency = 0;
for(E item : items) {
if(item.equals(o)) {
frequency++;
}
}
return frequency;
}
Here you List is NOT a raw type. References to generic type List should be parameterized which, it is. And this as well is open for any type of List (Integer, Long, String etc.)
Here is the code I'm using
public class aClass<T> {
private T[] elements;
public aClass(T[] elements) {
this.elements = elements;
}
public void doSomething() {
T[] newArray = (T[]) new Object[5];
...
}
}
I've seen people saying that creating an array like this is a bad idea, due to it being not type safe. However, every time I use it, I have no problems with it. When would creating an array like this cause a problem?
Thanks
Here is an example that causes issues:
public class Main {
public static class List<E extends Number> {
private E[] myE;
public void hello(E e) {
E[] newArray = (E[]) new Object[5];
for (int i = 0; i < newArray.length; i++) {
newArray[i] = e;
}
myE = newArray;
}
}
public static <T> T[] createArray(int size) {
return (T[]) new Object[size];
}
public static void main(String[] args) throws IOException {
List<Integer> integers = new List<Integer>();
integers.hello(5);
}
}
Your code works because when you declare your generic parameter <T> it is unbound, meaning that it extends Object. When you cast your array to (T[])you are actually casting to (Object[]) because that is the best the compiler can do. Now, if you keep your array inside your code, you should not have too many problems. But if somebody outside your code can retrieve that array and has instantiated your class with a type other than object, he will have ClassCastException.
You cannot create an array of T because Java does not know, at run time what is the type of T. This is due to the fact that in Java generics is implemented with type erasure. This means that the compiler discards most of the generic type information after ensuring everything is Ok.
With arrays the story is different, because Java needs to know the exact type of T in order to create the given array, and since such thing cannot be determined you cannot create an array of a generic type.
What you can do is to provide a instance of the actual array that you want to use, and the Java compiler can ensure it is of the appropriate type:
public static <T> void fillWith(T[] destiny, List<? extends T> source){
for(int i=0; i<= destiny.length; i++){
destiny[i] = source.get(i);
}
}
The java.utils.Arrays.copy method offers an alternative carefully using generics and reflections that you can use as a reference for what you want to do.
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
Something that is not type safe doesn't create problems in itself. But it can hide problems at compile time that won't popup until the right moment.
You would be able to fully work in a not type safe environment without having problems, but it's a bad habit just because a type safe environment does guarantee you that you won't have a set of common runtime errors, you don't have any at the moment but you don't have any guarantee, and these are really important, any safety you can trust into means less effort.
The other people are wrong. There is no other way to create the array unless you have an instance of the type at creation time. See also How to create a generic array in Java?
If you have the type instance (= something that has the type Class<T>), you can call java.lang.reflect.Array.newInstance(Class<?>, int) but even then, you'd need the cast, so why bother?
I'd say things were different if the type of elements was Object instead of T but since that's not the case, the code is perfectly OK.
If you want to hide this further, you can write a helper method:
#SuppressWarnings( "unchecked" )
public static <T> T[] createArray( int size ) {
return (T[]) new Object[size];
}
That creates an array without needing a cast when you call it.
It's safer to use a Collection in this cases. Just make something like
...
Collection<T> newElements = new ArrayList<T>(5);
...
Anyway, from what I known creating a generic array like this won't give you real problems, but "smells bad", as it requires explicit type casting. Do you really need an array in this case?
I've seen people saying that creating an array like this is a bad
idea, due to it being not type safe. However, every time I use it, I
have no problems with it. When would creating an array like this cause
a problem?
People say it is a bad idea because, logically, it is not correct to cast an object whose runtime type is Object[] to type T[] if T is not Object.
However, you do not see any immediate problems because, inside the class (within the scope of T), T is erased. So the cast from Object[] to T[] is not checked.
If all you ever do is use this variable inside your class (inside the scope of the type variable T), and you never return it to people outside of the class and there is no way people outside of the class can get access to it, then it will not cause any problems.
In fact, there are benefits to the way you're doing it -- you get all the benefits of generic type checking when getting and setting elements of the array, which you would not get if you simply followed the completely "type-safe" solution of using a variable of type Object[], in which case you would have to manually cast things you get out of it.
You will get a problem if you ever return this array variable to the outside (or otherwise allow the outside to access it) as type T[], because the calling code will expect a certain type of array, and the generics will insert a cast in the calling code when it receives this array, and the cast will fail at runtime (since e.g. a Object[] is not a String[]; they are different types at runtime). Here is a simple example:
public class aClass<T> {
private T[] elements;
public aClass(T[] elements) {
this.elements = elements;
}
public void doSomething() {
elements = (T[]) new Object[5];
...
}
public T[] getArray() {
return elements;
}
}
// some other code...
aClass<String> foo = new aClass<String>(new String[2]);
foo.doSomething();
String[] bar = foo.getArray(); // ClassCastException here
You may have problems with comparissions and printing.. basically any time that knowing the type is important for formatting. the way you have it the system have no clue what data is in the array
I have defined a Java function:
static <T> List<T> createEmptyList() {
return new ArrayList<T>();
}
One way to call it is like so:
List<Integer> myList = createEmptyList(); // Compiles
Why can't I call it by explicitly passing the generic type argument? :
Object myObject = createEmtpyList<Integer>(); // Doesn't compile. Why?
I get the error Illegal start of expression from the compiler.
When the java compiler cannot infer the parameter type by itself for a static method, you can always pass it using the full qualified method name: Class . < Type > method();
Object list = Collections.<String> emptyList();
You can, if you pass in the type as a method parameter.
static <T> List<T> createEmptyList( Class<T> type ) {
return new ArrayList<T>();
}
#Test
public void createStringList() {
List<String> stringList = createEmptyList( String.class );
}
Methods cannot be genericised in the same way that a type can, so the only option for a method with a dynamically-typed generic return type -- phew that's a mouthful :-) -- is to pass in the type as an argument.
For a truly excellent FAQ on Java generics, see Angelika Langer's generics FAQ.
.
.
Follow-up:
It wouldn't make sense in this context to use the array argument as in Collection.toArray( T[] ). The only reason an array is used there is because the same (pre-allocated) array is used to contain the results (if the array is large enough to fit them all in). This saves on allocating a new array at run-time all the time.
However, for the purposes of education, if you did want to use the array typing, the syntax is very similar:
static <T> List<T> createEmptyList( T[] array ) {
return new ArrayList<T>();
}
#Test
public void testThing() {
List<Integer> integerList = createEmptyList( new Integer[ 1 ] );
}
#pauldoo
Yes, you are quite right. It is one of the weaknesses with the java generics imho.
I response to Cheekysoft I'd like to propose to also look at how it is done by the Java people themselves, such as T[] AbstractCollection#toArray(T[] a). I think Cheekysofts version is superior, but the Java one has the advantage of familiarity.
Edit: Added link.
Re-edit: Found a bug on SO :)
Follow-up on Cheekysoft:
Well, as it is a list of some type that should be returned the corresponding example should look something like:
static <T> List<T> createEmptyList( List<T> a ) {
return new ArrayList<T>();
}
But yes, passing the class object is clearly the better one. My only argument is that of familiarity, and in this exact instance it isn't worth much (in fact it is bad).