Array of Generic List

2019-01-01 11:33发布

问题:

I am playing with Generic and arrays, it seems the following code compiles fine,

ArrayList<Key> a = new ArrayList<Key>();

But the compiler complains about this one,

ArrayList<Key>[] a = new ArrayList<Key>[10];

By reading post in stackoverflow, I sort of understand that this is due to Type Erasure and I can fix it by using,

ArrayList<Key>[] a = (ArrayList<Key> []) new ArrayList[10];

or list of list

ArrayList<ArrayList<Key>> b = new ArrayList<ArrayList<Key>>();

But I can\'t figure out the reason behind the scene. Especially, why the second one is illegal given the first one is perfectly OK. And why the compiler does not complain about the list of list.

回答1:

You can\'t have an array, because an array requires a raw type. You typecast it in the second instance, which makes it fit the defined type, and is therefore legal (however, this is impossible for it to infer). The list of list is legal as ArrayList isn\'t an array.

Read chapter 7.3 (page 15) in the official tutorial for more details on this.

The component type of an array object may not be a type variable or a parameterized type, unless it is an (unbounded) wildcard type.You can declare array types whose element type is a type variable or a parameterized type, but not array objects. This is annoying, to be sure. This restriction is necessary to avoid situations like:

List<String>[] lsa = new List<String>[10]; // not really allowed
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[1] = li; // unsound, but passes run time store check
String s = lsa[1].get(0); // run-time error - ClassCastException

If arrays of parameterized type were allowed, the example above would compile without any unchecked warnings, and yet fail at run-time.

The tutorial then goes on to say the following:

Since type variables don’t exist at run time, there is no way to determine what the actual array type would be. The way to work around these kinds of limitations is to use class literals as run time type tokens



回答2:

I had a similar question myself - FWIW, I didn\'t find the answers persuasive. The pertinent section from the most detailed answer (referring to the pdf reference) is this:

The component type of an array object may not be a type variable or a parameterized type, unless it is an (unbounded) wildcard type.You can declare array types whose element type is a type variable or a parameterized type, but not array objects. This is annoying, to be sure. This restriction is necessary to avoid situations like

       List<String>[] lsa = new List<String>[10]; // not really allowed
       Object o = lsa;
       Object[] oa = (Object[]) o;
       List<Integer> li = new ArrayList<Integer>();
       li.add(new Integer(3));
       oa[1] = li; // unsound, but passes run time store check
       String s = lsa[1].get(0); // run-time error - ClassCastException

So because I can cat the List[] to Object[], then shove something incorrect into the Object[], then refer to incorrectly from the List reference, through the casted ref, this is bad/disallowed? But only with new?

It\'s still more than a bit obscure to me how declaring this with new is any more or less of a problem than the usage, still crossing my eyes staring at it in the hope that it will start to make sense, or at least resolve into a nice 3d image.



回答3:

Array was poor man\'s generics; with real generics, one should avoid arrays, though not always possible.

Arrays are covariant, generics are invariant; combined with erasure, things just don\'t fit very well, as illustrated by the example in Chris\'s answer.

However I think it is possible to relax the spec to allow generic array creation - there\'s really no problem there. The danger comes when up casting the array; a compiler warning at that point is enough.

Actually Java does create generic arrays for vararg methods, so it\'s a little hypocritical.

Here are utility methods taking advantage of that fact

@SafeVarargs
static <E> E[] arrayLiteral(E... array)
{
    return array;
}

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
    return Arrays.copyOf(array, length);
}

// usage

    List<String>[] array1 = arrayLiteral(list, list);

    List<String>[] array2 = newArray(10);


回答4:

Creating generic arrays isn\'t type-safe (see \"Item 25: Prefer lists to arrays\" of \"Effective Java - second edition\" by Joshua Bloch).

Use:

 List<List<Key>> b = new ArrayList<List<Key>>(10);

Or with Java SE 7:

 List<List<Key>> b = new ArrayList<>(10);


回答5:

The arrays allow to escape type checks (as illustrated in the Chris\'s answer). So, you could have a code which passes all compiler checks (no \"unchecked\" warnings from compiler), but fail at run time with ClassCastException. Forbidding this construction raises the problem for a developer, so warnings do appear.



标签: java generics