9

Suppose I have a generic class with a generic parameter T which is a Number subclass. I would like to initialize an array of T during class construction. Is it possible? If yes how? If not why?

public class AClass<T extends Number>{

    private T array[];
    private int arrayOfInt[];

    public AClass(int size){
        arrayOfInt = new int[size];
        array = ? //what should I put here?
    } 
} 
Heisenbug
  • 38,762
  • 28
  • 132
  • 190

4 Answers4

9

T is only know at compile time. It is not know at runtime and thus you cannot initilise the contents of the array. However you can create the array, every value will be null.

array = (T[]) new Number[size];

EDIT: The problem with creating instances of any type is you need to know what is the default value you want and which constructor you want to call. e.g. there is no new Double()

As mentioned below, double[] will be more efficient and faster than Number[] and unless you need large long values, it will be able to store every possible value.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • If I do this way, after having initialized the array with Number[size], can I do array[1] = new Double()? (supposing T is Double of course). – Heisenbug Aug 28 '11 at 16:35
  • This will give you a ClassCastException. You need to use reflection to initialize the array after being passed an object of the same type as T (not a subclass) – Ryan Amos Aug 28 '11 at 18:27
  • @Ryan Amos: not if it doesn't get passed into code outside of the class. within the class, T is erased – newacct Aug 28 '11 at 20:35
  • @newacct Are you sure about that? I know I've had problems with this same issue in the past and I think I tried this solution and it gave me a ClassCastException. – Ryan Amos Aug 28 '11 at 20:37
  • @Overdose, To create an instance of a class you need to have a `Class` reference available at runtime. BTW: Using an array of Number is extremely inefficient. It is likely to be much faster and more compact if you just use `double[]` even if you only need `Short[]` – Peter Lawrey Aug 28 '11 at 22:03
  • Using `double[]` when you actually want to store BigInteger might not be the best idea :-p – Paŭlo Ebermann Aug 28 '11 at 22:19
  • @Paŭlo Ebermann, The same applies to BigDecimal and AtomicLong/Integer. There is no elegant solution to initialising the array. You have to have a massive if/else block or pass a builder object. – Peter Lawrey Aug 28 '11 at 22:24
  • 1
    @0verbose you were tagged but not tagged properly. Just typing this so you're notified. – Ryan Amos Aug 29 '11 at 00:48
  • @Ryan Amos: what do you mean with "you were tagged but not tagged properly"? – Heisenbug Aug 29 '11 at 09:14
  • @0verbose, I called you Overbose with an O instead of an 0 ;) – Peter Lawrey Aug 29 '11 at 12:40
  • 1
    @Peter Lawrey: oh yes. I see. A moreover a "d" instead of a "b". :) – Heisenbug Aug 29 '11 at 14:05
4

If you want to use arrays, there are two options:

  1. Peter Lawrey's answer, array = (T[]) new Number[size];. You have to make sure never to return or pass this variable to code outside of the class that expect it to be an array of a particular type, which will cause an exception.

  2. Declare array as type Number[], then just do array = new Number[size];. The downside of this is that when you get anything out of it you will need to explicitly cast to T to use it as such.

The two are the same after type erasure, and they will both cause unchecked cast warning, so it's really a matter of personal preference. The former is more convenient, while the latter is more formally correct (you are not pretending it's a type it's not).

Alternately, some people will tell you to use an ArrayList<T> instead. But internally, an ArrayList is still implemented using one of these two options.

newacct
  • 119,665
  • 29
  • 163
  • 224
2

This is not possible.

Because Java generics use type erasure, the type of T isn't known at runtime, so you can't create an array of it.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
0

Other options than mentioned are to use toArray(T[]) or java.lang.reflect.Array:

public class AClass<T extends Number>{
    private T array[];

    public AClass(final int size, T[] a){
        array = (new ArrayList<T>() {{
            for (int i = 0; i < size; i++) {
                add(null);
            }
        }}).toArray(a);
    }

    public AClass(int size, Class<T[ ]> clazz) {  
        array = clazz.cast(java.lang.reflect.Array.newInstance(
            clazz.getComponentType( ), size));  
    }  

    public static void main(String[] args) {
        System.out.println("toArray: "
                + new AClass<Double>(42, new Double[]{}).array.length);
        System.out.println("java.lang.reflect.Array: "
                + new AClass<Double>(42, Double[].class).array.length);
    }
} 

PS. solution using reflection is close to one suggested in Langer's Generics FAQ (Utilities.createBuffer): How do I generically create objects and arrays?

Community
  • 1
  • 1
gnat
  • 6,213
  • 108
  • 53
  • 73
  • Why on earth would you do that? You could just look at `ArrayList`'s solution and implement that. – Ryan Amos Aug 28 '11 at 18:28
  • @RyanAmos I don't follow sorry - can you elaborate? – gnat Aug 28 '11 at 18:45
  • Here's ArrayList's source code. Instead of allocating new memory and creating an unrelated object to accomplish the task, simply use the code from the object to solve your problem: http://www.docjar.com/html/api/java/util/ArrayList.java.html – Ryan Amos Aug 28 '11 at 19:41
  • @RyanAmos I see. Why on earth would I copy the library method code instead of invoking it? search the web for _copy paste anti-pattern_. Somehow, "allocating new memory and creating an unrelated object" scare me less than your suggestion – gnat Aug 28 '11 at 20:15
  • Use `ArrayList`'s methods to solve your problem. The solution already exists, and you're looking at the right place, but you're going at it the wrong way. What you should be doing is using reflection to obtain `a`'s type, then creating an array of proper size. That's exactly what `ArrayList.toArray(T)` does. – Ryan Amos Aug 28 '11 at 20:27
  • this is awkward because it forces you to make an array with that type and pass it in as an additional argument – newacct Aug 28 '11 at 20:38
  • @RyanAmos do you mean solution like [described here in details](http://stackoverflow.com/questions/529085/java-how-to-generic-array-creation/4221845#4221845)? if yes thanks but no thanks. I considered it before answering and still prefer to just invoke _toArray_ instead – gnat Aug 28 '11 at 20:40
  • @newacct what way would look less _awkward_ to you? – gnat Aug 28 '11 at 20:43
  • @newacct That's the only way to do it if you're passing out. That's what you said, if I understood correctly. – Ryan Amos Aug 28 '11 at 20:57
  • gnat - You still end up with bad code that doesn't work better. If you're going to do bad code, it should work better, IMO. If it works worse and there's no obvious reason NOT to use good code, then use good code. – Ryan Amos Aug 28 '11 at 20:58
  • @Ryan Amos: yes, but if you're passing it out, I would rather put it as an argument to the particular method that passes out (i.e. only when necessary), rather than the whole class's constructor. Consider `ArrayList`; it uses an array internally and doesn't require you to pass anything in initially. Only its `toArray()` method with 1 argument (which returns a T[]) requires you to pass something in. In most cases, I think classes like this will not need to pass the array out. – newacct Aug 28 '11 at 21:03
  • @RyanAmos somehow, our notions of _good, bad, better_ seem to be opposite here. I can live with that - can you? – gnat Aug 28 '11 at 21:24
  • 2
    gnat - Yes, I can. That's why I didn't do -1. – Ryan Amos Aug 28 '11 at 21:29
  • 1
    @gnat: Did you actually try your solution? Your code creates a length-0 array, not an array of length `size`. – Paŭlo Ebermann Aug 28 '11 at 22:23