40

Suppose I have to create an array which stores ArrayList's of Integers and the array size is 10.

The below code will do it:

ArrayList<Integer>[] pl2 = new ArrayList[10]; 

Question 1:

In my opinion the more appropriate code would be

ArrayList<Integer>[] pl2 = new ArrayList<Integer>[10];    

Why does this not work?

Question 2:

Both of the below compile

  1. ArrayList<Integer>[] pl2 = new ArrayList[10];
  2. ArrayList[] pl3 = new ArrayList[10];

What is the difference as far as the reference declaration of pl2 and pl3 is concerned?

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Geek
  • 21,584
  • 19
  • 68
  • 85
  • 9
    If you want type safety, use a List>. Arrays and generics don't play well together. – JB Nizet May 07 '13 at 09:14
  • Why u need to use an Arraylist when you know the size already.A simple array will work fine to store ten integers – Neo May 07 '13 at 13:35
  • @PaulBellora: because not enough people vote to close as duplicate (and yes, that includes *you* ;-)) – Joachim Sauer May 07 '13 at 15:27
  • @PaulBellora: sounds like we need (to find?) a set of high-quality answers for these kinds of questions to mark-as-duplicate. I'm currently to tired to verify the wrongness of the linked-to-question, but it can very well be true. – Joachim Sauer May 07 '13 at 20:02
  • @JoachimSauer Okay here are some duplicates (in chrono order): [\[1\]](http://stackoverflow.com/questions/217065) [\[2\]](http://stackoverflow.com/questions/470198) [\[3\]](http://stackoverflow.com/questions/749425) [\[4\]](http://stackoverflow.com/questions/3975054) [\[5\]](http://stackoverflow.com/questions/5662394) [\[6\]](http://stackoverflow.com/questions/7810074) And of course my favorite because I answered it: http://stackoverflow.com/questions/15957325 Voting to reopen so we can choose a better duplicate. – Paul Bellora May 08 '13 at 03:41
  • @JoachimSauer and Paul, you can always flag with "other" and explain your reason to a mod...instead of going through the *whole process* of reopening and then closing as duplicate (which seems so much more extensive and fragile). – Jesse May 08 '13 at 04:05

11 Answers11

16

The generic info only matters in compile time, it tells the compiler which type could be put into an array, in runtime, all the generic info will be erased, so what matters is how you declare the generic type.

Quoted from Think in Java:

it’s not precisely correct to say that you cannot create arrays of generic types. True, the compiler won’t let you instantiate an array of a generic type. However, it will let you create a reference to such an array. For example:

List<String>[] ls; 

This passes through the compiler without complaint. And although you cannot create an actual array object that holds generics, you can create an array of the non-generified type and cast it:

//: arrays/ArrayOfGenerics.java 
// It is possible to create arrays of generics. 
import java.util.*; 

public class ArrayOfGenerics { 
    @SuppressWarnings("unchecked") 
    public static void main(String[] args) { 
        List<String>[] ls; 
        List[] la = new List[10]; 
        ls = (List<String>[])la; // "Unchecked" warning 
        ls[0] = new ArrayList<String>(); 
        // Compile-time checking produces an error: 
        //! ls[1] = new ArrayList<Integer>(); 

        // The problem: List<String> is a subtype of Object 
        Object[] objects = ls; // So assignment is OK 
        // Compiles and runs without complaint: 
        objects[1] = new ArrayList<Integer>(); 

        // However, if your needs are straightforward it is 
        // possible to create an array of generics, albeit 
        // with an "unchecked" warning: 
        List<BerylliumSphere>[] spheres = 
           (List<BerylliumSphere>[])new List[10]; 
        for(int i = 0; i < spheres.length; i++) 
           spheres[i] = new ArrayList<BerylliumSphere>(); 
    } 
}

Once you have a reference to a List[], you can see that you get some compile-time checking. The problem is that arrays are covariant, so a List[] is also an Object[], and you can use this to assign an ArrayList into your array, with no error at either compile time or run time.

If you know you’re not going to upcast and your needs are relatively simple, however, it is possible to create an array of generics, which will provide basic compile-time type checking. However, a generic container will virtually always be a better choice than an array of generics.

ltebean
  • 1,009
  • 6
  • 15
  • 5
    can you please explain more. I understand erasure but not in this context. – Geek May 07 '13 at 09:24
  • 1
    ... so? This doesn't explain why that code doesn't compile. The compiler could use or simply ignore that information and compile without problems. – Bakuriu May 07 '13 at 11:41
  • 1
    "Arrays and generics do not mix well. We cannot instantiate arrays of parameterized types. Erasure removes the parameter type information, and arrays must know the exact type that they hold, in order to enforce type safety." Quated from Thinking in Java – ltebean May 07 '13 at 12:28
13

Question 1:

Basically, this is forbidden by Java language. This is covered in Java Language Specification for generics.

When you use

ArrayList<Integer>[] pl2 = new ArrayList[10];    // warning

you get the compiler warning, because the following example will compile (generating warning for every line of code):

ArrayList wrongRawArrayList = new ArrayList();      // warning
wrongRawArrayList.add("string1");                   // warning 
wrongRawArrayList.add("string2");                   // warning  

pl2[0] = wrongRawArrayList;                         // warning 

but now you array, that supposed to contain ArrayList of Integer, contains totally wrong ArrayList of String objects.

Question 2:

As it was already answered, declaration of p12 provides you with compile time checking and frees you from using casting when getting items from your ArrayList.

Slightly modified previous example:

ArrayList<Integer>[] pl2 = new ArrayList[10];                // warning 

ArrayList<String> wrongArrayList = new ArrayList<String>();  // OK!
wrongArrayList.add("string1");                               // OK! 
wrongArrayList.add("string2");                               // OK!

pl2[0] = wrongArrayList;                                     // ERROR

Now, since you are using generics, this won't compile. But if you use

ArrayList[] pl2 = new ArrayList[10]; 

you will get the same result as in the first example.

Vladimir
  • 9,215
  • 6
  • 32
  • 56
4

Arrays are covariant. That means they retain the type of their elements at runtime. Java's generics are not. They use type erasure to basically mask the implicit casting that is going on. It's important to understand that.

You need to use Array.newInstance()

In addition, arrays carry runtime type information about their component type, that is, about the type of the elements contained. The runtime type information regarding the component type is used when elements are stored in an array in order to ensure that no "alien" elements can be inserted.

For more details look here

Abimaran Kugathasan
  • 26,826
  • 11
  • 67
  • 101
2

Let's start with question 2 first and then get back to question 1:

Question 2:

> ArrayList[] pl2 = new ArrayList[10]; ArrayList[] pl3 = new ArrayList[10];

What is the difference as far as the reference declaration of p12 and p13 is concerned?

In pl2 ensures better type safety than p13.

If I write for pl2:

pl2[0]=new ArrayList<String>();

it will give me a compiler error stating "cannot convert from ArrayList<String> to ArrayList<Integer>"

Thus it ensures compile time safety.

However if I write for p13

pl3[0]=new ArrayList<String>();
pl3[1]=new ArrayList<Integer>();

it will not throw any error and the onus will be on the developer to code and check properly while extracting data from p13, to avoid any unsafe type conversion during runtime.

Question 1:

That's just probably the way generics work. During the main array initialization, ArrayList<Integer>[] pl2 = new ArrayList[10], the left hand side, ArrayList<Integer>[] pl2, will ensure type safety only when you initialize the ArrayList object in the index position:

pl2[0]=new ArrayList<Integer>();

The right hand side main array declaration = new ArrayList[10] just ensures that the index position will hold ArrayList type items. Also have a look at type erasure concepts in Type Erasure for more information.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Rohit
  • 633
  • 4
  • 9
2

This does not work because generic classes does not belong to Reifiable Types.

The JLS about Array creation expression states :

It is a compile-time error if the [class type] does not denote a reifiable type (§4.7). Otherwise, the [class type] may name any named reference type, even an abstract class type (§8.1.1.1) or an interface type (§9).

The rules above imply that the element type in an array creation expression cannot be a parameterized type, other than an unbounded wildcard.

The definition of Reifiable Types is :

Because some type information is erased during compilation, not all types are available at run time. Types that are completely available at run time are known as reifiable types.

A type is reifiable if and only if one of the following holds:

It refers to a non-generic class or interface type declaration.

It is a parameterized type in which all type arguments are unbounded wildcards (§4.5.1).

It is a raw type (§4.8).

It is a primitive type (§4.2).

It is an array type (§10.1) whose element type is reifiable.

It is a nested type where, for each type T separated by a ".", T itself is reifiable.

For example, if a generic class X<T> has a generic member class Y<U>, then the type X<?>.Y<?> is reifiable because X<?> is reifiable and Y<?> is reifiable. The type X<?>.Y<Object> is not reifiable because Y<Object> is not reifiable.
Community
  • 1
  • 1
alain.janinm
  • 19,035
  • 10
  • 56
  • 103
1

Question 1.

Well, it's not the correct syntax. Hence that does not work.

Question 2.

ArrayList<Integer>[] pl2 = new ArrayList[10];
ArrayList[] pl3 = new ArrayList[10];

Since pl2 is defined with generic type <Integer> at compile time, the compiler will be know that pl2 is only allowed to have Integers and if you try to assign somthing other than Integers you will be alerted and compilation will fail.

In pl3 since there is no generic type you can assign any type of object to the list.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
pulasthi
  • 1,720
  • 1
  • 17
  • 27
0

Problems with generics are by default issued as a warning by the compiler.

After compilation, because of type erasure, they all become ArrayList[] pl2 = new ArrayList[10], but the compiler warns you that this is not good.

Generics have been added to Java, and to be backwards compatible you can use generic with non-generic interchangeably.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Michal Borek
  • 4,444
  • 2
  • 27
  • 39
0
 ArrayList<Integer>[] pl2 = new ArrayList<Integer>[10];

Means you don't need to do casting when you retrive data from the ArrayList example in normal case

 ArrayList[] pl2 = new ArrayList[10];
 pl2.put(new Integer(10));
 Integer i = p12.get(0);    // this is wrong
 Integer i = (Integer)p12.get(0);    // this is true with casting

but

 ArrayList<Integer>[] pl2 = new ArrayList<Integer>[10];   
 pl2.put(new Integer(10));
 Integer i = p12.get(0);    // this is true no need for casting
aymankoo
  • 635
  • 5
  • 10
0

Question1

You cannot create arrays of parameterized types

Question 2

 ArrayList<Integer>[] pl2 = new ArrayList[10];

It means you are telling to compiler that you are going to create array which will store arraylist of integers. Your arraylist will only contain Integer objects. That's where generics comes in. Generics make your code more safer and reliable. If you are sure your list should only contain integer objects, you should always go ahead with this.

But when you say

 ArrayList[] pl3 = new ArrayList[10];

it means arraylist can store any object type like string, integer, custom objects, etc.

Community
  • 1
  • 1
M Sach
  • 30,322
  • 72
  • 198
  • 300
0

It seems like you cannot create an array of arraylists with a generic type, according to an answer to Stack Overflow question Create an array of ArrayList elements.

Community
  • 1
  • 1
Genjuro
  • 6,457
  • 7
  • 34
  • 59
0

As far as I know, in Java there are no such things as generics. In terms of types, ArrayList<Integer> and ArrayList are the same things.

Java uses type erasure for generics. It means that all type information about the generic is erased at compile time. So ArrayList<Integer> become ArrayList.

So it's just a compile-time trick. I am guessing, to avoid any confusions or mistakes that the programmer might do, they allowed ArrayList<Integer>[] to be instantiated like this: new ArrayList[10].

So an ArrayList<Integer>[] and a ArrayList[] are the same thing because the information in brackets is erased at compile time.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Alecu
  • 2,295
  • 2
  • 24
  • 45