319

What's the reason why 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?

Hearen
  • 7,420
  • 4
  • 53
  • 63
devoured elysium
  • 101,373
  • 131
  • 340
  • 557
  • 39
    What are you talking about? You can absolutely do this in .NET. -- I'm here trying to figure out why I can't do it in Java. – BrainSlugs83 Oct 05 '14 at 07:22
  • @BrainSlugs83 - please add a link to some code example or tutorial which shows that. – MasterJoe Apr 03 '20 at 22:57
  • Also see - https://stackoverflow.com/questions/21577493/why-not-create-an-object-and-cast-to-a-generic-type-whats-the-solution – MasterJoe Apr 06 '20 at 00:15
  • 1
    @MasterJoe2 the above code in the OP's question is what I'm referring to. It works fine in C#, but not in Java. -- The question states it works in neither, which is incorrect. -- Not sure there's value in discussing it further. – BrainSlugs83 Apr 06 '20 at 02:24
  • [Because it's a bug that hasn't been fixed yet. ](https://i.stack.imgur.com/hlOJI.jpg) – Meech Dec 11 '20 at 02:54

16 Answers16

250

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
  • 119,665
  • 29
  • 163
  • 224
  • 34
    But what about erasure? Why doesn't that apply? – Qix - MONICA WAS MISTREATED Mar 05 '13 at 08:22
  • 17
    How does `ArrayList ` do it then? – Thumbz Mar 25 '14 at 23:55
  • 13
    @Thumbz: You mean `new ArrayList()`? Generic types do not contain the type parameter at runtime. The type parameter is not used in creation. There is no difference in the code generated by `new ArrayList()` or `new ArrayList()` or `new ArrayList()` at all. – newacct Mar 26 '14 at 00:05
  • 11
    I was asking more about how `ArrayList` works with its' `private T[] myArray`. Somewhere in the code, it must have an array of generic type T, so how? – Thumbz Mar 26 '14 at 01:02
  • 23
    @Thumbz: It doesn't have an array of runtime type `T[]`. It has an array of runtime type `Object[]`, and either 1) the source code contains a variable of `Object[]` (this is how it is in the latest Oracle Java source); or 2) the source code contains a variable of type `T[]`, which is a lie, but doesn't cause problems due to `T` being erased inside the scope of the class. – newacct Mar 26 '14 at 02:44
  • 1
    @Thumbz: yes, if you go with route #2 – newacct Mar 26 '14 at 08:43
  • @newacct How odd. Aren't references == pointers, which would mean they're all the same size (the size of an address)? If this is true, then the size should already be known, and if not, it seems you could just say something like `class CustomArray { TYPE [] myArray = new TYPE [42]; }` and it should be able to do exactly the same thing without giving any warnings. – Thumbz Mar 26 '14 at 09:24
  • 3
    @Thumbz: You can *access* array elements without any difference. But if you get the type of the array object itself, it will be wrong. For various reasons, Java arrays are reified (they know their component type at runtime), and `String[]` implies the array it points to actually has runtime component type `String` or subtype thereof, not just that it's an array that contains `String`s. It's been that way from the first version of Java, before Generics, and cannot be changed. – newacct Mar 26 '14 at 18:26
  • 1
    @newacct: I'm not totally sure I follow, but I still don't see why ` []` is a very different concept from `String []`. If you know at least one class from which TYPE inherits from, it seems that the compiler should be able to read `class la { T [] lala = new T[42]; }` and equivocate that to `{ Object [] lala = new Object[42]; }` – Thumbz Mar 27 '14 at 19:23
  • @Thumbz: In Java, `String[] foo = (String[])new Object[5];` is fine at compile time but causes an exception at runtime. It's just a historical fact of how arrays in Java were made that `String[]` and `Object[]` are two different classes at runtime. `new Object[42]` creates an object of runtime class `Object[]`, and it's not safe to consider that as `T[]` because if you have a method that exposes that to the outside of the class as type `T[]`, like `T[] getArray{ return lala; }`, and someone calls that on a `CustomArray` and assigns the result to a `String[]`, they will get an exception – newacct Mar 28 '14 at 04:48
  • If it causes an exception at run time, then how is it done in `ArrayList`? There must be some way of creating a `T[]` that doesn't cause exceptions. – Thumbz Mar 29 '14 at 18:26
  • @Thumbz: What makes you think there needs to be a `T[]`? Like I said, they could 1) have the variable of type `Object[]`, and cast to `T` when they get an element out of it, or 2) have the variable of type `T[]` but it's actually still of runtime class `Object[]`, and be careful not to expose it to the outside of the class. The two approaches are the same after erasure. – newacct Mar 29 '14 at 20:22
  • Why do you not know T at runtime? – committedandroider Dec 17 '14 at 23:48
  • @committedandroider: where would you get it from at runtime? – newacct Dec 18 '14 at 01:13
  • Say we have the List ADT, When the client initializes ArrayList(and runs the program), he or she does it by List list = new ArrayList ? Basically, you get when it is being used? – committedandroider Dec 18 '14 at 03:53
  • @committedandroider: Nope. That is exactly the same as `List list = new ArrayList();` – newacct Dec 18 '14 at 09:55
  • Oh so user doesn't have to specify type parameter when initializing? – committedandroider Dec 18 '14 at 18:20
  • @committedandroider: The user doesn't have to specify a type parameter, and specifying the type parameter doesn't change the compiled byte code. It's only used for compile-time type checking only. – newacct Dec 18 '14 at 19:12
  • The answer is not clear. In fact the topic need separate blog post to be explained. See my next comment. – Oleksandr Papchenko Nov 09 '16 at 12:16
  • The problem is deeper than pointed in that answer, so further investigation is needed. As you said type info is erased and in compiled code we have no difference between two generic types - all we have is base type - so why for T[] - compile to Object[]. In this case everything will be fine - array will remember that it was created with object type and will let save all types. However as for me the real problem is that arrays are covariant meaning that Animal[] can be assigned to Object[]. in other hand generics are not ArrayList can not be assigned to ArrayList – Oleksandr Papchenko Nov 09 '16 at 12:17
  • This is how i understand the topic at the time - that can be wrong. If you think this is wrong - please give me reference to the documentation or other source of truth :) – Oleksandr Papchenko Nov 09 '16 at 12:18
  • why do python do not have such problems ? – Rushikesh Jul 03 '20 at 14:55
152

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&forumID=316

Community
  • 1
  • 1
Bart Kiers
  • 166,582
  • 36
  • 299
  • 288
  • 3
    Note that I made it a CW since the answer is not mine. – Bart Kiers May 28 '10 at 07:55
  • 12
    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 May 29 '10 at 23:56
  • 2
    You still can use `new Box>[n]`, which might be sometimes sufficient, although it wouldn't help in your example. – Bartosz Klimek Jul 02 '12 at 07:58
  • 1
    @BartKiers I don't get it... this still does not compile (java-8) : `Box[] bsa = new Box[3];` did anything change in java-8 and up I assume? – Eugene Mar 12 '18 at 12:32
  • 1
    @Eugene, Arrays of specific generic types simply aren't allowed becuase they can lead to a loss of type safety as demonstrated in the sample. It is not allowed in any version of Java. The answer starts as "Arrays of generic types are not allowed because they're not sound. " – garnet Jan 01 '19 at 13:56
  • @BartKiers - Can we do this instead "T[ ] elements = new T[num]" ? – MasterJoe Apr 05 '20 at 23:52
66

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 (assuming T extends Object and not another class)

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

I prefer the first example, however more academic types seem to prefer the second, or just prefer not to think 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 this use-case cannot be supported without warnings, something is fundamentally broken with the generics model IMHO.

Rany Albeg Wein
  • 3,304
  • 3
  • 16
  • 26
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 6
    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 May 30 '10 at 00:01
  • 7
    I voted this answer down because your preferred example is not permitted in Java and your second example may throw a ClassCastException – Polyana Fontes Jan 31 '14 at 02:27
  • 5
    @JoséRobertoAraújoJúnior It is quite clear the first example needs to be replaced with the second example. It would be more helpful for you to explain why the second example can throw a ClassCastException as it wouldn't be obvious to everyone. – Peter Lawrey Feb 01 '14 at 08:48
  • 4
    @PeterLawrey I created a self-answered question showing why `T[] ts = (T[]) new Object[n];` is a bad idea: http://stackoverflow.com/questions/21577493/why-not-create-an-object-and-cast-to-a-generic-type-whats-the-solution – Polyana Fontes Feb 05 '14 at 12:40
  • @MarkoTopolnik I downvoted him because 1) The OP asked why he can't do that in java and not how he could do, so he didn't answered the question. 2) He said his answer has 2 example because he prefer the first example but has actually only one. 3) He's only example doesn't create a T[] but creates an Object[] that can cause issues. My self-answered question is not related to this question but is related to the question Peter did, he asked me to explain why he's example can throw an exception because it was not clear, so I explained in a different question – Polyana Fontes Feb 05 '14 at 12:57
  • @MarkoTopolnik The OP is not asking opinions, he's asking explanation why he can't do `new T[initialCapacity]` when `T` is a generic class, he's asking this because he don't understand why java disallows it. Also, _alternative disign choice which someone would have liked better_ would be a chatty topic and would be against stackoverflow rules [You should only ask practical, answerable questions based on actual problems that you face. Chatty, open-ended questions diminish the usefulness of our site and push other questions off the front page.](http://stackoverflow.com/help/dont-ask) – Polyana Fontes Feb 05 '14 at 13:13
  • 1
    @MarkoTopolnik I should be given a medal for answering all your comments to explain the same thing I've already said, the only thing that changed from my original reason is that I though that he said `T[] ts = new T[n];` was a valid example. I'll keep the vote because he's answer can cause issues and confusions to other devs and is also off-topic. Also, I'll stop comment about this. – Polyana Fontes Feb 05 '14 at 13:32
37

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 instantiated.

Motomotes
  • 4,111
  • 1
  • 25
  • 24
Durandal
  • 19,919
  • 4
  • 36
  • 70
21

The answer was already given but if you already have an Instance of T then you can do this:

T t; //Assuming you already have this object instantiated or given by parameter.
int length;
T[] ts = (T[]) Array.newInstance(t.getClass(), length);

Hope, I could Help, Ferdi265

Ferdi265
  • 2,879
  • 1
  • 17
  • 15
  • 1
    This is a nice solution. But this will get unchecked warnings (cast from Object to T[]). Another "slower" but "warning-free" solution would be: `T[] ts = t.clone(); for (int i=0; i – midnite Jul 20 '13 at 17:46
  • 1
    In addition, if what we kept is `T[] t`, then it would be `(T[]) Array.newInstance(t.getClass().getComponentType(), length);`. i did spend some times to figure out `getComponentType()`. Hope this helps others. – midnite Jul 26 '13 at 16:42
  • 1
    @midnite `t.clone()` will not return `T[]`. Because `t` is not Array in this answer. – xmen Sep 25 '14 at 02:02
6

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

There's a good overview here.

GaryF
  • 23,950
  • 10
  • 60
  • 73
  • I don't see how you could support "new T[5]" even with invariant arrays. – Dimitris Andreou May 29 '10 at 08:19
  • 3
    @DimitrisAndreou Well, the whole thing is rather a comedy of errors in the Java design. It all started with array covariance. Then, once you have array covariance, you can cast `String[]` to `Object` and store an `Integer` in it. So then they had to add a runtime type check for array stores (`ArrayStoreException`) because the issue could not be caught at compile-time. (Otherwise, an `Integer` actually could be stuck in a `String[]`, and you would get an error when you tried to retrieve it, which would be horrible.) ... – Resigned June 2023 Dec 24 '14 at 15:46
  • 3
    @DimitrisAndreou … Then, once you have put a runtime check in place of a far sounder compile-time check, you run into type erasure (also an unfortunate design flaw -- included only for backward compatibility). Type erasure means that you _can't_ do runtime type checks for generic types. So therefore, to avoid the array storage type problem, you simply can't have generic arrays. If they had simply made arrays invariant in the first place, we could just do compile-time type checks without running afoul of erasure. – Resigned June 2023 Dec 24 '14 at 15:49
  • … I have just discovered the five-minute editing period for comments. `Object` should have been `Object[]` in my first comment. – Resigned June 2023 Dec 24 '14 at 15:59
3

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?

Community
  • 1
  • 1
emory
  • 10,725
  • 2
  • 30
  • 58
  • 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 May 28 '10 at 11:28
  • Hopefully you get a type-safety warning from the compiler, yes? – Matt McHenry May 28 '10 at 11:34
  • 1
    Yes, I get a type-safety warning. Yes, I see that my example is not responsive to the question. – emory May 28 '10 at 11:51
  • 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. 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 May 29 '10 at 08:34
  • 1
    You did not create a generic array here. The compiler created a (non-generic) array for you. – newacct May 29 '10 at 23:57
  • For a second, i was reassuring myself what I am seeing couldn't be possible....but anyway self realisation kicks in....for future readers...the array store exception did not occur because after type erasure you could see that the underlying array is plain Box[] and the data you stored into box array was Box in both cases...ie the generics were to instruct the content of box(members) to be of specified type but not the box itself(class)....so as I said in the first array of boxes had boxes instance, then you casted the array into object type(covariant) and again assigned box type... – mahee96 Jun 16 '21 at 20:18
  • If only you had assigned a non box type to covariant Object[] array using object array reference ex: oa[0] = new String("different object"); then you would have got an array store exception.... – mahee96 Jun 16 '21 at 20:23
  • Why did u get a type cast exception eventually? It is because you earlier instructed using generics at compile time that your bsa[] array was of generic type String since arrays are covariant the compiler which looked at the generics assumes you will always store Strings into that array. Hence when accessing bsa[0].x didn't warrant for a compilation error. – mahee96 Jun 16 '21 at 20:40
  • But what you actually did was you tricked the compiler into a fool by re-assigning the array reference to it's super type Object[] and exploited the fact that object array can hold all its subtype object behind the back of compiler. So compiler couldn't verify this and assumed you are still trying to access the string type which it assumed you stored earlier.... – mahee96 Jun 16 '21 at 20:44
  • All these are compile time validation and hence these tricks actually bypassed compiler validation....but at runtime...when actual instances are being validated....by RTTI or runtime type info checker ....you get caught that the assignment was invalid since String reference can't really hold an Integer instance....Hence the CLASS CAST EXCEPTION – mahee96 Jun 16 '21 at 20:47
  • Very interesting fact is THIS CODE HERE MIGHT SEEM YOU CREATED A GENERIC ARRAY, but what you actually did was to create an Array of Box which internally holds generic items, as op said, – mahee96 Jun 16 '21 at 20:52
  • A generic array is something like T[] arr = new T[10]; but this is IMPOSSIBLE still as of java 11, because due to TYPE ERASURE the type T is erased at compile time and only base type Object remains at runtime....so if the T generic type was assigned to various types such as Integer, String, Double etc....then at compile time there needs to be separate classes for all those types....this is how it is done in C++ templates.... – mahee96 Jun 16 '21 at 20:57
  • But in java only one base class is present and it is the generic type info erased class ex: erases to type Object, erases to type Number....also by rules of class hierarchy, covariance, upcasting doesn't require an explicit cast....this is the reason erasure works without casts.... – mahee96 Jun 16 '21 at 21:00
  • On the other hand downcasting from higher type to lower type requires explicit cast since we wouldn't be sure which exact class or instance we are looking at, ex: custom class X in far future might extend Object, might not be known to the compiler beforehand, hence it forcefully asks for an explicit type cast. – mahee96 Jun 16 '21 at 21:04
  • The same is what compiler does in the name of type erasure, erases the generic info and keeps the base type...then creates appropriate explicit casts in the byte code since it knows what generic type the src/tgt reference/instances resolve into....based on its current inference over entire code which uses this generics.... – mahee96 Jun 16 '21 at 21:08
  • BUT very important thing to NOTE is that IT IS NOT THE COMPILER WHICH CREATES THE INSTANCES IN HEAP but the RUNTIME engine or java VM which creates the instances hence for it to do a generic instantiation of objects it requires this info at runtime evaluation. But all it has is the Base type Object for generic type T at runtime....hence it creates Instance of Object type.... – mahee96 Jun 16 '21 at 21:12
  • Now you could ask WHY NOT THE COMPILER THEN JUST INTRODUCE THE GENERIC TYPE CAST (T) by itself for the newly created Object instance?.....The ANSWER is the Object instance is half baked state of any subtype...ie we can get object(simple type) from say string(complex type) instance but getting string(complex) from an half baked or incomplete simple type Object is IMPOSSIBLE.... because Object doesn't even know it has a successor called String but a very complex type than Object... – mahee96 Jun 16 '21 at 21:19
  • This concludes the fact that any subtype instance cannot be recreated from base type and the only info after type erasure at runtime is the base type.... – mahee96 Jun 16 '21 at 21:21
  • Don't forget that if instead of base type the compiler was instructed to create real generic object/instances by substituting the Generic class name, it would then mean the same generic class can no more be re-used by other sections of code wich specifies different generic type....if the compiler did that then it would be overwriting the Type of object to be instantiated in the byte code for each new encounter of generic type assignment of same class.....AS I SAID EARLIER, THIS IS HOW C++ TACKLES IT, by having many copies of same TEMPLATE CLASS FOR DIFFERENT generic/template type invocation. – mahee96 Jun 16 '21 at 21:28
  • In C++ this leads to code duplication at compile time and at runtime there are many flavors of class types to choose from for same template class.... – mahee96 Jun 16 '21 at 21:29
  • Java generics is good, but using it must come with proper knowledge....ex: T arr = (T) new Object; and T[] arr = (T[]) new Object[size]; (explicit casting) are due to above mentioned reasons... – mahee96 Jun 16 '21 at 21:33
  • NOTE: a generic type is as much useless than just specifying Object as base type for reference when used in arrays(actual objects/instances must of target type T ex: String) coz both require explicit type cast when retrieving data from the container... – mahee96 Jun 16 '21 at 21:36
  • Another interesting fact is for ARRAYS, Java generics allows T[] arr; ie, ALLOWS EXTENDS because of the COVARIANT nature but DENIES SUPER () because for the container will surely contain subtypes only which do not require cast when writing into container(this is allowed since subtypes contain atleast base type info + additional info)..on the other hand is not allowed, since supertype ex: (incomplete or simple) Object instance cannot be interpreted as Number (subtype) anytime..not enough info about subtype is present in base – mahee96 Jun 16 '21 at 21:50
  • As for Java Collections, the language designers must have thought that by restricting more, the Heap pollution caused by covariance of array could be avoided, hence no more ArrayStoreException... The only change required was to make Collections Invariant, ie covariance of array is disallowed....ex array of Animal could contain cat, dog, etc but List of Animal must contain only animals and not subtypes such as cats dogs or supertypes of Animal... – mahee96 Jun 16 '21 at 22:05
  • The Invariance property is however not much useful, hence to bring back the covariance property for subtype relationship or extends U> was required in this case subtype T instances can be assigned to base type U without a cast. Similarly additional property of contravariance for supertype relationship or super> was introduced.... – mahee96 Jun 16 '21 at 22:47
  • A List might have Zoo, Animals,...down until Cat...A List might have Cat, Animal...up until Zoo. For 'extends' we can minimum guarantee it might be up-until zoo(supertype of T) since T must be constructed only after Zoo is constructed. For 'super' we can maximum guarantee we can only insert new types down-until Cat and not any lower than that. – mahee96 Jun 16 '21 at 23:00
  • Based on the fact that any subtype has its supertype as a part of itself, subtype to supertype doesn't require explicit casting...This same fact is used for both extends/super. – mahee96 Jun 16 '21 at 23:03
  • Just that the bounds are different for 'extends'... upper bound is U and Lowe bound is T....so any T can atleast DISPLAY its internal U ..... similarly for 'super'...upper bound is T and lower bound is U...so any T can ACCEPT U because it must atleast contain T. – mahee96 Jun 16 '21 at 23:10
  • Following this, PECS (Producer(Output - U to T) Extends and Consumer(Input - T to U) Super) from/to a collection respectively is used for generics variance definition. – mahee96 Jun 17 '21 at 04:30
3

In my case, I simply wanted an array of stacks, something like this:

Stack<SomeType>[] stacks = new Stack<SomeType>[2];

Since this was not possible, I used the following as a workaround:

  1. Created a non-generic wrapper class around Stack (say MyStack)
  2. MyStack[] stacks = new MyStack[2] worked perfectly well

Ugly, but Java is happy.

Note: as mentioned by BrainSlugs83 in the comment to the question, it is totally possible to have arrays of generics in .NET

David Airapetyan
  • 5,301
  • 4
  • 40
  • 62
3

From Oracle tutorial:

You cannot create arrays of parameterized types. For example, the following code does not compile:

List<Integer>[] arrayOfLists = new List<Integer>[2];  // compile-time error

The following code illustrates what happens when different types are inserted into an array:

Object[] strings = new String[2];
strings[0] = "hi";   // OK
strings[1] = 100;    // An ArrayStoreException is thrown.

If you try the same thing with a generic list, there would be a problem:

Object[] stringLists = new List<String>[];  // compiler error, but pretend it's allowed
stringLists[0] = new ArrayList<String>();   // OK
stringLists[1] = new ArrayList<Integer>();  // An ArrayStoreException should be thrown,
                                            // but the runtime can't detect it.

If arrays of parameterized lists were allowed, the previous code would fail to throw the desired ArrayStoreException.

To me, it sounds very weak. I think that anybody with a sufficient understanding of generics, would be perfectly fine, and even expect, that the ArrayStoredException is not thrown in such case.

Stick Hero
  • 31
  • 1
  • This is true that genericized arrays cannot be created not due to the fact that type erasure is in place...since the types are erased at compile time, new instantiation of List class is possible only without its generic type...hence – mahee96 Jun 16 '21 at 22:34
  • how can one allow such usage...one way would be to stop doing type erasure + retain different versions of the class byte code for different generic types as in C++ templates....then the above syntax of genericized array instantiation is completely valid............List[] arrayOfLists = new List[2]; // is completely valid since the List interface itself is the only things that remains after generic paramter type info is erased by the compiler. – mahee96 Jun 16 '21 at 22:40
2

class can declare an array of type T[], but it cannot directly instantiate such an array. Instead, a common approach is to instantiate an array of type Object[], and then make a narrowing cast to type T[], as shown in the following:

  public class Portfolio<T> {
  T[] data;
 public Portfolio(int capacity) {
   data = new T[capacity];                 // illegal; compiler error
   data = (T[]) new Object[capacity];      // legal, but compiler warning
 }
 public T get(int index) { return data[index]; }
 public void set(int index, T element) { data[index] = element; }
}
Devanshu
  • 883
  • 10
  • 22
1

It is because generics were added on to java after they made it, so its kinda clunky because the original makers of java thought that when making an array the type would be specified in the making of it. So that does not work with generics so you have to do E[] array=(E[]) new Object[15]; This compiles but it gives a warning.

Alvin
  • 11
  • 1
0

If we cannot instantiate generic arrays, why does the language have generic array types? What's the point of having a type without objects?

The only reason I can think of, is varargs - foo(T...). Otherwise they could have completely scrubbed generic array types. (Well, they didn't really have to use array for varargs, since varargs didn't exist before 1.5. That's probably another mistake.)

So it is a lie, you can instantiate generic arrays, through varargs!

Of course, the problems with generic arrays are still real, e.g.

static <T> T[] foo(T... args){
    return args;
}
static <T> T[] foo2(T a1, T a2){
    return foo(a1, a2);
}

public static void main(String[] args){
    String[] x2 = foo2("a", "b"); // heap pollution!
}

We can use this example to actually demonstrate the danger of generic array.

On the other hand, we've been using generic varargs for a decade, and the sky is not falling yet. So we can argue that the problems are being exaggerated; it is not a big deal. If explicit generic array creation is allowed, we'll have bugs here and there; but we've been used to the problems of erasure, and we can live with it.

And we can point to foo2 to refute the claim that the spec keeps us from the problems that they claim to keep us from. If Sun had more time and resources for 1.5, I believe they could have reached a more satisfying resolution.

Sufian
  • 6,405
  • 16
  • 66
  • 120
ZhongYu
  • 19,446
  • 5
  • 33
  • 61
0

As others already mentioned, you can of course create via some tricks.

But it's not recommended.

Because the type erasure and more importantly the covariance in array which just allows a subtype array can be assigned to a supertype array, which forces you to use explicit type cast when trying to get the value back causing run-time ClassCastException which is one of the main objectives that generics try to eliminate: Stronger type checks at compile time.

Object[] stringArray = { "hi", "me" };
stringArray[1] = 1;
String aString = (String) stringArray[1]; // boom! the TypeCastException

A more direct example can found in Effective Java: Item 25.


covariance: an array of type S[] is a subtype of T[] if S is a subtype of T

Hearen
  • 7,420
  • 4
  • 53
  • 63
0

T vals[]; // OK

But, you cannot instantiate an array of T // vals = new T[10]; // can't create an array of T

The reason you can’t create an array of T is that there is no way for the compiler to know what type of array to actually create.

0

There surely must be a good way around it (maybe using reflection), because it seems to me that that's exactly what ArrayList.toArray(T[] a) does. I quote:

public <T> T[] toArray(T[] a)

Returns an array containing all of the elements in this list in the correct order; the runtime type of the returned array is that of the specified array. If the list fits in the specified array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the specified array and the size of this list.

So one way around it would be to use this function i.e. create an ArrayList of the objects you want in the array, then use toArray(T[] a) to create the actual array. It wouldn't be speedy, but you didn't mention your requirements.

So does anyone know how toArray(T[] a) is implemented?

Eliran Malka
  • 15,821
  • 6
  • 77
  • 100
Adam
  • 713
  • 1
  • 8
  • 16
  • 3
    List.toArray(T[]) works because you are essentially giving it the component type T at runtime (you are giving it an instance of the desired array type, from which it can get the array class, and then, the component class T). With the actual component type at runtime, you can always create an array of that runtime type using `Array.newInstance()`. You'll find that mentioned in many question that ask how to create an array with a type unknown at compile time. But the OP was specifically asking *why* you can't use the `new T[]` syntax, which is a different question – newacct Nov 23 '11 at 22:22
-2

Try this:

List<?>[] arrayOfLists = new List<?>[4];
  • This actually works and does not throw any compile time errors. Can anyone please explain this? – Aditya Mayukh Som Jul 16 '23 at 14:58
  • @AdityaMayukhSom Also `List[] array = new List[4]` would work, because "List" is reifiable. That is, it's a "real" type at runtime even after erasure. Per [JLS 15.10](https://docs.oracle.com/javase/specs/jls/se17/html/jls-15.html#jls-15.10) the type in an array creation expression must be "reifiable." Per [JLS 4.7](https://docs.oracle.com/javase/specs/jls/se17/html/jls-4.html#jls-4.7) a type may be reifiable if type parameters are wildcards. The wildcards mean you don't know what it's a List of, limiting what you can do to safe operations the same way raw types do. – Ben Aug 04 '23 at 23:19