0

I've found out that if you have a generic class with an array that depends of type parameter of that class, you can't initialize that array in a usual way:

class Foo<T>  {
    private T[] a;

    Foo()  {
       a=new T[5]; //doesn't compile
    }
}

You can only do something like this:

class Foo<T>  {
        private T[] a;

        Foo(T[] a)  {
           this.a=a;
        }
    }

But this code is possible:

ArrayList<Integer> list=new ArrayList<>();

Why? How does the ArrayList overcome that thing? I've looked into its code but couldn't figure it out. It seems like it just stores things in Object[] array which seems wrong.

parsecer
  • 3,076
  • 4
  • 35
  • 78
  • If it was wrong, ArrayList would work. But it works. So it's not wrong. Why do you think it's wrong? – JB Nizet Apr 02 '17 at 19:51
  • @JB Nizet, Because what's the point of using `Object[]` inside when we can do just that even without generics. Doesn't the usefulness of generics lies in possibility to store exactly type `T`? – parsecer Apr 02 '17 at 19:52
  • 2
    Since `T` is erased at compilation time such code `new T[size]` would need to be compiled as `new Object[size]` which leaves us with array able to hold *any* object. But in second case compiler is able to detect if we are passing array *which already exist* and its type matches specified `T` type. – Pshemo Apr 02 '17 at 19:53
  • See also [Java generics - type erasure - when and what happens](http://stackoverflow.com/questions/339699/java-generics-type-erasure-when-and-what-happens). – Andy Thomas Apr 02 '17 at 19:55
  • @parsecer generics are erased at runtime, which make new T[] impossible. But you shouldn't care about the type of the inner array, because it's private, and thus not part of the public API. What matters is that the list is typesafe, and that the compiler prevents adding a String to a List. Where and how the list internally stores that Integer doesn't matter. – JB Nizet Apr 02 '17 at 19:55

2 Answers2

3

The array inside ArrayList is always of type Object[], because you can store anything inside such an array. The type argument enables type safety none the less on a method level and this is what matters.

Edit to address the topic of type erasure as requested:

Type erasure in Java means that generic type arguments are only present during compile time, where it matters most to ensure type safety. Generics have been implemented this way to keep newer versions of Java compatible to older ones.

The drawback of this is that one can never instantiate a generic type, because it is never known at runtime.

Brian
  • 842
  • 8
  • 16
  • Covering type erasure is critical to a good answer to this question. – Andy Thomas Apr 02 '17 at 19:56
  • Then why in all examples do they store separate variables like `T a` in type `T`, not `Object`? – parsecer Apr 02 '17 at 20:03
  • 1
    @parsecer: Because a T[] differs from a T. Java generics don't always play nicely with arrays. See also [How to create a generic array in Java?](http://stackoverflow.com/questions/529085/how-to-create-a-generic-array-in-java). – Andy Thomas Apr 02 '17 at 20:09
2

Consider this as an analogy:

You own a pencil company. You drive around with pencils in your van.

You could put flowers in your van: it's a van, after all, so you could put anything you want (that fits) in there. Baseballs, gorillas, helium balloons.

But you choose not to: you're a pencil company, so all you put into your van is pencils. If your friend from the burger company asks you to put some burger patties in your van, you will say no: this van is only for pencils.

So, when you come to take something out of your van, you know it's going to be a pencil, because you made sure the only thing that went in through those doors was a pencil.


And so it is with an ArrayList<T>: you could store anything in that Object[], but you don't: you can only store Ts in there, because you can only add things to it through the add(T) or addAll(Collection<? extends T>) methods.

So it doesn't matter that your elements are being stored in an Object[], rather than more-specifically typed array: the only things that you'll get out of an ArrayList<T> - via its get() (etc) methods - are instances of T, or one of its subtypes.

Andy Turner
  • 122,430
  • 10
  • 138
  • 216
  • So, it's basically a deal with a compiler, isn't it? If we don't use generics and just use `Object` compiler can let us put anything there, and we would only get run-time exception if we take variable of type `B` thinking it's type `A`? But since the deal exists, compiler allows us to avoid direct type casting, is that right? – parsecer Apr 02 '17 at 20:07
  • 2
    Exactly. Generics are an entirely compile-time construct. At runtime, it's just an `ArrayList`, and you can put what you like in there. – Andy Turner Apr 02 '17 at 20:08
  • In many book examples they use `T` for storing variables or operate with the array's content (the ArrayList does that too). But why? If it's a given `Object` in the array is `T` what need is there to call it `T` anywhere inside the class, apart from the class declaration? – parsecer Apr 02 '17 at 20:10
  • 1
    The whole point about generics is elision of casts. You can do everything without generics - and, pre Java 5, we did. You just had to do a lot of explicit casting yourself, and reap the problems of casting to the wrong type. You *can* do everything without referencing `T`, but it's harder to verify type safety at compile time. So, *wherever you can*, use `T` rather than `Object`. – Andy Turner Apr 02 '17 at 20:12
  • 1
    The "wherever you can" is important, because, for example, you can't create a `List[]` via the `Array.newInstance(Class, int)` method, because there is no `Class>` to pass as a parameter, only `Class` (via `List.class`). And you don't *really* need it, because you can store anything in an `Object[]`, with control over what is stored in the array. – Andy Turner Apr 02 '17 at 20:17