views:

760

answers:

6

Suppose I have this:

class test<T>
{
    private T[] elements;
    private int size;
    public test(int size)
    {
        this.size = size;
        elements = new T[this.size];
    }
}

It seems this isn't possible because the compiler doesn't know what constructor to call once it tries to replace the generics code or something. What I'm wondering is, how would I go about doing this? I imagine it is possible, given how easily done it is in C++.

Edit: Sorry I forgot the [] in the elements declaration.

A: 

I may be wrong, but your declaration seems strange, shouldn't you have :

private T[] elements;
mat
+9  A: 

The problem is that since the generic type parameter T is transformed into Object by the compiler (it's called type erasure), you actually create an array of Object. What you can do is provide a Class<T> to the function:

class test<T>
{
    private T[] elements;
    private int size;
    public test(Class<T> type, int size)
    {
        this.size = size;
        elements = (T[]) Array. newInstance(type, size);
    }
}

You will find a better exlanation of it here: Angelika Langer - Can I create an array whose component type is a type parameter?

Johannes Schaub - litb
+7  A: 

The easiest alternative to a generic array is to use a list.

Dan Dyer
I agree - Lists are generally a better alternative for many reasons. In fact, some people will suggest that arrays should be considered a deprecated part of the language in Java 5+. I'm not saying I'm one of them...but you will find that opinion.
Alex Miller
Arrays are still valid for primitive types. But for reference types it's just an inappropriate optimisation almost all the time.
Tom Hawtin - tackline
+1  A: 

There are two solutions that do not require passing in the class object. In both I am assuming that since it's a private variable, you are the only person putting things into it, and the outside world doesn't see it.

One is to just use a plain Object array Object[]. The disadvantage is that you will lose the benefits of generics and you will have to cast things coming out of it, which makes the code uglier. But since you are the only person putting things into it, then the cast should always be safe. The outside doesn't need to know what's going on.

The other options is kind of a hack. You create an Object[], and then "cast" it to T[]. Technically this is illegal, because Object[] and T[] are different runtime types and the former cannot be cast to the latter. But as long as you don't pass the array out of the class (e.g. return it or something), then it won't cause any problems, because T[] will get erased to Object[] anyway, and no cast is actually performed. Then you can get the benefits of generics in your class for easier coding, but you have to be extremely careful not to pass that array to anyone who actually expects a T[], because it is not.

newacct
A: 

I tend to avoid basic arrays and use ArrayLists (or similar Lists) wherever possible for a bunch of reasons. The two most important are:

  1. I don't need the semantics of the bald array. There's nothing about the bracket notation (i.e., shorthand for pointer arithmetic) that makes my life easier.

  2. If I use Lists, I'm setting myself up to use more sophisticated concurrency-enabled structures down the line, as needed. For example, it's dead easy to replace an ArrayList with a CopyOnWriteArrayList.

So, writing your example using a generic ArrayList would look something like this:

class test<T> {
    /** It's not strictly necessary to make this a List vs. an ArrayList
        You decide what interface you really need to expose internally. */
    private List<T> elements;
    /** This parameter isn't necessary with a List.  You can always call the
        size() method instead. */
    private int size;
    public test(int size) {
        this.size = size;
        elements = new ArrayList<T>();
        // Note that the next line isn't strictly necessary.
        // An ArrayList can be dynamically resized without any real effort on your part.
        elements.ensureCapacity(size); 
    }
}
Bob Cross
A: 

The size field does appear redundant. You could just have elements.length instead of size.

class Test<T> {
    private final T[] elements;
    public test(int size) {
        elements = (T[]) new Object[size];
    }
}

Or you could replace the Test class with an ArrayList. i.e. drop the class altogether.

Peter Lawrey
this will throw an exception since new array is not of type T[].
pdeva
@pdeva: no it won't; as long as it stays inside the class Test, it won't throw any exceptions because the erasure of T[] is Object[]; but you have to be careful not to let it leave the class
newacct