2

In the Java tutorials ( http://docs.oracle.com/javase/tutorial/extra/generics/fineprint.html ) I saw the following:

// Not really allowed.
List<String>[] lsa = new List<String>[10];
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
// Unsound, but passes run time store check
oa[1] = li;

// Run-time error: ClassCastException.
String s = lsa[1].get(0);
If arrays of parameterized type were allowed, the previous example
would compile without any unchecked warnings, and yet fail at run-time


// OK, array of unbounded wildcard type.
List<?>[] lsa = new List<?>[10];
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
// Correct.
oa[1] = li;
// Run time error, but cast is explicit.
String s = (String) lsa[1].get(0);

They then explained that if we switch List<String>[] lsa = new List<String>[10]; to List<?>[] lsa = new List<?>[10]; then it's okay but we have to upcast.

A proffessor of mine asked the following about this: "Why does the latter compile?"

He then gave the answer: "When the argument is ? the meaning is that every cell in the array can include an ArrayList. Because there aren't any assumptions about the type of the generic class the previous exception can't happen."

It still doesn't really make sense to me that the the wildcard one works while the previous one doesn't. If we had to enforce upcasting in the wildcard example why couldn't we do it in the first example as well?

I'd appreciate if someone could clear this up for me.

Shookie
  • 4,925
  • 6
  • 31
  • 56
  • possible duplicate of [What's the issue with creating a generic array?](http://stackoverflow.com/questions/18581002/whats-the-issue-with-creating-a-generic-array) – Rohit Jain Oct 18 '13 at 19:17

3 Answers3

1

The wild-card one forces you to cast, so it's up to you to know the real class.

The first case (if possible) would give you a false sense of security and no warnings, since the whole point of generics is to allow you to work without constantly casting things.

Kayaman
  • 67,952
  • 3
  • 69
  • 110
0

Generics were designed to be type-safe in case you provide a certain type. But due to type erasure, the information of which type exactly can be stored in your generic list is lost. This would have the same effect as if you would just provide ? as the type. At runtime it is not sure that the types will always be correct and thus the compiler complains.

The data in both variations will be the same but one variation compiles, the other does not. This is because when providing the wildcard, you basically turn off the type-safety of the compiler like a switch and tell it that you will care about it yourself. If it will fail, it will be your own fault, and not the compilers fault anymore. :)

noone
  • 18,807
  • 5
  • 58
  • 75
0

The actual reason that the first one is disallowed is because java's type system is unsound.

The spec says that if S is-a T, then S[] is-a T[]. Unfortunately this rule causes type problems, to wit:

void unsoundness( Animal[] aanl ) {
    // causes a runtime type check that the element types are compatible
    aanl[ 0 ] = new Animal(); 
}

Dog[] adog = new Dog[ 1 ];
unsoundness( adog );

With generics and type erasure, that runtime type check can't make the correct determination.

void unsoundness( List< ? extends Animal >[] alstr ) {
    alstr[ 0 ] = new ArrayList< Animal >();
}

List< Dog >[] alobj = new List< Dog >[ 1 ]; // fictitious
unsoundness( alobj );
Judge Mental
  • 5,091
  • 15
  • 20