hfhc2
hfhc2

Reputation: 4391

Generic array of complex types

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?

Upvotes: 0

Views: 1243

Answers (2)

newacct
newacct

Reputation: 122449

There are two issues here.

  1. 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];
        }
    }
    
  2. 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
    

Upvotes: 1

Paul Boddington
Paul Boddington

Reputation: 37645

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

Upvotes: 1

Related Questions