2

I have the following code

public class Container<T> {
    private T element;
    private T[] tarray;

    public T getElement() {
        return element;
    }

    public void setElement(T element) {
        this.element = element;
    }

    public void add(T element) {
        tarray[0] = element;
    }

    public void CreateArray(int size) {
        //Can this be cleaned up better?
        tarray = (T[]) new Object[size];
    }

    public T get() {
        return tarray[0];
    }

    public Container(T someElement) {
        this.element = someElement;
    }

    public Container() {
    }

    public static void main(String[] args) {
        Container<String> myContaier1 = new Container<String>();
        myContaier1.setElement("Hello");
        myContaier1.CreateArray(1);
        myContaier1.add("GoodBye");
        System.out.println(myContaier1.get());
    }
}

Is there no way to initialize a type safe generic array?

Perception
  • 75,573
  • 19
  • 170
  • 185
stackoverflow
  • 15,804
  • 43
  • 121
  • 181
  • 1
    Duplicate of http://stackoverflow.com/questions/529085/java-how-to-generic-array-creation – Aubin Feb 11 '13 at 20:18
  • It's effectively Object[] (just as well all standard containers like ArrayList), so threat it like one. – bestsss Feb 11 '13 at 20:30

5 Answers5

6

There is no way unless you provide a reified T in the form of an actual Class<T> object that represents a specific value of T. This is because the array type is reified, whereas the Generic type isn't.

Marko Topolnik
  • 179,046
  • 25
  • 276
  • 399
  • 3
    Interestig word "reified". What does that mean? Is this a special Generics vocabulary? – AlexWien Feb 11 '13 at 20:17
  • 1
    @AlexWien It's a general term that also applies to the discussion about Generics. It means the runtime can observe it, as opposed to only the compiler. It means you can refer to it, pass it as an argument, have a method return it, store it in a variable, etc. – Marko Topolnik Feb 11 '13 at 20:18
1

There are two problems here:

First of all, the actual type of your array will always be Object[]. You cast it to T[], but this works only because T[] erases to Object[]. If your class definition said, for example, <T extends Number>, then (T[])new Object[] would fail with a ClassCastException.

You could get around this by passing Class<T> to the constructor of your collection and keeping it in a field:

private Class<T> componentClass;
...
tarray = (T[]) Array.newInstance(componentClass, size);

Now the actual in-memory type of tarray is T[], but you still get an unchecked cast error. Even though you have the component class, as far as I know there is no equivalent of Class.cast() for doing a checked cast of an array instance.

Russell Zahniser
  • 15,480
  • 35
  • 29
  • "If your class definition said, for example, , then (T[])new Object[] would fail with a ClassCastException." Right, but then in that case, it would be `(T[])new Number[]`. Basically, whatever T[] gets erased to. – newacct Feb 12 '13 at 02:27
-1

You can do private T[] tarray;, But you cannot assign it to (T[]) new Object[size];. How can an array of Object be same an array of any other class. T is not even there after compilation. It is called type erasure. E.g if you do

List<Person> persons = new ArrayList<Person>();

It becomes List persons = new ArrayList() after compilation.

fastcodejava
  • 35,219
  • 24
  • 124
  • 181
  • what do you mean "cannot assign". What do you think will happen if you assign `(T[]) new Object[size];` to `tarray`? – newacct Dec 20 '13 at 10:25
-1

It is similar to ask "Is there a way to initialize a generic object: new T()?"

Of course it is impossible, as the compiler does not know what the type is of it.

Array is the same, its type relies on its elements. If the type of its elements is unknown, the type of itself is unknown.

Those classes which can take generic types like List, Set, Map, etc. are different. They have their own classes as types and they are dynamic, so you can initialize one like new ArrayList<T>().


You can try on this:

public class Test {
    public static void main (String args[]) {
        int[] i = new int[3];
        short[] s = new short[4];
    ArrayList<String> a = new ArrayList<String>();

        System.out.println(i.getClass().getName());
        System.out.println(s.getClass().getName());
        System.out.println(args.getClass().getName());
        System.out.println(a.getClass().getName());
    }
}

You will see the types of elements are already combined with the arrays, while not combined with ArrayList.

shuangwhywhy
  • 5,007
  • 2
  • 14
  • 27
-1

There are way to do this on the JVM, but to do something like this in Java would require writing a lot of boiler plate code. In Scala, you can use Manifests to get around type erasure, and can instantiate generic arrays without casting.

An example:

class Container[T : Manifest]() { 
  def createArray(size:Int) = Array.ofDim[T](size) 
}

scala> val f = new Container[String]
f: Container[String] = Container@12f3aa66

scala> f.createArray(5)
res7: Array[String] = Array(null, null, null, null, null)

Scala code compiles to the same bytecode class files as Java (implying that this should be possible in Java if you jumped through enough hoops and maybe wrote your own manifests). Scala classes can be imported into Java projects... though I'm not sure how hard it is to instantiate a class like this from Java.

If you often find yourself wanting to be able to handle more complicated type situations, have a look at Scala, you might like it.

nairbv
  • 3,618
  • 1
  • 21
  • 25