tags:

views:

215

answers:

7

What's the reason Java doesn't allow us to do

private T[] elements = new T[initialCapacity];

?

I could understand .NET didn't allow us to do that, as in .NET you have value types that at run-time can have different sizes, but in Java all kinds of T will be object references, thus having the same size(correct me if I'm wrong).

What is the reason?

A: 

The main reason is due to the fact that arrays in Java are covariant.

There's a good overview here.

GaryF
I don't see how you could support "new T[5]" even with invariant arrays.
Dimitris Andreou
+11  A: 

Quote:

Arrays of generic types are not allowed because they're not sound. The problem is due to the interaction of Java arrays, which are not statically sound but are dynamically checked, with generics, which are statically sound and not dynamically checked. Here is how you could exploit the loophole:

class Box<T> {
    final T x;
    Box(T x) {
        this.x = x;
    }
}

class Loophole {
    public static void main(String[] args) {
        Box<String>[] bsa = new Box<String>[3];
        Object[] oa = bsa;
        oa[0] = new Box<Integer>(3); // error not caught by array store check
        String s = bsa[0].x; // BOOM!
    }
}

We had proposed to resolve this problem using statically safe arrays (aka Variance) bute that was rejected for Tiger.

-- gafter

(I believe it is Neal Gafter, but am not sure)

See it in context here: http://forums.sun.com/thread.jspa?threadID=457033&amp;forumID=316

Bart Kiers
Note that I made it a CW since the answer is not mine.
Bart Kiers
This explains why it might not be typesafe. But type safety issues could be warned by the compiler. The fact is that it is not even possible to do it, for almost the same reason why you cannot do `new T()`. Each array in Java, by design, stores the component type (i.e. `T.class`) inside it; therefore you need the class of T at runtime to create such an array.
newacct
+2  A: 

The reason this is impossible is that Java implements its Generics purely on the compiler level, and there is only one class file generated for each class. This is called Type Erasure.

At runtime, the compiled class needs to handle all of its uses with the same bytecode. So "new T[capacity]" would have absolutely no idea what type needs to be instanciated.

Durandal
+1: I was about to leave a comment to Bark K.'s answer about this, when I noticed a different answer already listed it.
R. Bemrose
+1  A: 

I like the answer indirectly given by Gafter. However, I propose it is wrong. I changed Gafter's code a little. It compiles and it runs for a while then it bombs where Gafter predicted it would

class Box<T> {
    final T x;
    Box(T x) {
        this.x = x;
    }
}

class Loophole {
    public static < T > T [ ] array ( final T ... values )
    {
    return ( values ) ;
    }

    public static void main(String[] args) {
    Box<String> a = new Box ( "Hello" ) ;
    Box<String> b = new Box ( "World" ) ;
    Box<String> c = new Box ( "!!!!!!!!!!!" ) ;
        Box<String>[] bsa = array ( a , b , c ) ;
    System . out . println ( "I created an array of generics." ) ;
        Object[] oa = bsa;
        oa[0] = new Box<Integer>(3); 
    System . out . println ( "error not caught by array store check" ) ;
    try
        {
        String s = bsa[0].x;
        }
    catch ( ClassCastException cause )
        {
        System . out . println ( "BOOM!" ) ;
        cause . printStackTrace ( ) ;
        }
    }
}

The output is

I created an array of generics.
error not caught by array store check
BOOM!
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at Loophole.main(Box.java:26)

So it appears to me you can create generic array types in java. Did I misunderstand the question?

emory
Your example is different from what I've asked. What you described are the dangers of array covariance. Check it out (for .NET : http://blogs.msdn.com/b/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two-array-covariance.aspx )
devoured elysium
Hopefully you get a type-safety warning from the compiler, yes?
Matt McHenry
Yes, I get a type-safety warning.Yes, I see that my example is not responsive to the question.
emory
Actually you get multiple warnings due to sloppy initialization of a,b,c. Also, this is well known and affects the core library, e.g. <T>java.util.Arrays.asList(T...). If you pass any non-reifiable type for T, you get a warning (because the created array has a less precise type than the code pretends), and it's super ugly. It would be better if *the author* of this method got the warning, instead of emitting it at usage site, given that the method itself is safe, it doesn't expose the array to the user.
Dimitris Andreou
You did not create a generic array here. The compiler created a (non-generic) array for you.
newacct
+1  A: 

It's because Java's arrays (unlike generics) contain, at runtime, information about its component type. So you must know the component type when you create the array. Since you don't know what T is at runtime, you can't create the array.

newacct
Brief and exact.
Dimitris Andreou
A: 

Despite Java arrays are considered like Collections in practice, in theory they are of type java.lang.Object, that's why they don't have generics.

pabiagioli
Collections are java.lang.Object too :)
Dimitris Andreou
http://java.sun.com/docs/books/jls/second_edition/html/arrays.doc.html . Here says "The direct superclass of an array type is Object. Every array type implements the interfaces Cloneable and java.io.Serializable". In the other hand, a List, Vector, ArrayList and the rest of collections (which DO have generics) extend from a class Collection. i.e. = public interface List<E> extends Collection<E> a List is a Collection (which is an Object too, but it's not its direct superclass) and an array is an Object.
pabiagioli
There's the Map interface too. but it's not a true collection. Here's the reference:http://java.sun.com/docs/books/tutorial/collections/interfaces/index.html
pabiagioli
A: 

By failing to provide a decent solution, you just end up with something worse IMHO.

The common work around is as follows.

T[] ts = new T[n];

is replaced with

T[] ts = (T[]) new Object[n];

I prefer the first example, however more acedemic types seem to prefer the second, or just prefer not to thing about it.

Most of the examples of why you can't just use an Object[] equally apply to List or Collection (which are supported), so I see them as very poor arguments.

Note: this is one of the reasons the Collections library itself doesn't compile without warnings. If you this usecase cannot be supported without warnings, something is fundermentally broken with the generics model IMHO.

Peter Lawrey
You have to be careful with the second one. If you return the array created in such a way to someone who expects, say, a `String[]` (or if you store it in a field that is publicly accessible of type `T[]`, and someone retrieves it), then they will get a ClassCastException.
newacct