0

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?

Community
  • 1
  • 1
hfhc2
  • 3,705
  • 2
  • 22
  • 44
  • 6
    for me it looks like you want to implement your own arraylist class. is it any reason why you are doing that, or why way how that class was implemented is not suitable for you? – user902383 Sep 05 '15 at 22:08
  • 1
    Just use an `Object[]` as underlying data structure. Or use `ArrayList` in the first place and drop your own implementation. – Fabian Barney Sep 05 '15 at 22:15
  • Why need `Array.newInstance` if you can simply pass a ready to use array? You can also use `new SimpleArray<>(Outer.class, 10)` (no matter if the class is inner or not) and then cast the result to a parameterized type, say `@SuppressWarnings({"rawtypes", "unchecked"}) SimpleArray> supplierSimpleArray = (SimpleArray) new SimpleArray<>(Outer.class, 10)` (you can't parameterize class literals, you must "blind" the compiler to strip the parameterization making the cast legal, and you can suppress the warnings letting the compiler know that this cast is safe). – Lyubomyr Shaydariv Sep 05 '15 at 22:33

2 Answers2

1

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));
Paul Boddington
  • 35,031
  • 9
  • 56
  • 107
1

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
    
newacct
  • 110,405
  • 27
  • 152
  • 217