11

Looking into another question I bumped into this intriguing behavior of the 1.8.0_112 Sun-Oracle compiler (I have not tested with others):

import java.util.List;

interface Alpha<T> {
   List<Integer> intList();
}

interface Beta {
   List<Integer> intList();
}

class Main {

   public static void main(String[] args) {

      Alpha rawAlpha = null;
      Alpha<Character> charAlpha = null;
      Alpha<?> qmAlpha = null;
      Beta beta = null;

      for (Integer i : charAlpha.intList()) {}
      for (Integer i : qmAlpha.intList()) {}
      for (Integer i : beta.intList()) {}
      for (Integer i : rawAlpha.intList()) {}
   }
}

The compiler only fails at the last for loop:

error: incompatible types: Object cannot be converted to Integer
      for (Integer i : rawAlpha.intList()) {}
                                       ^
1 error

So despite that intList() return list type List<Integer> in Alpha does not depend on the type parameter T, it seems that the <Integer> is erased at compilation time.

Notice that if we declare a non-generic interface Beta that would be, in theory, equivalent to making reference to the raw Alpha, there is no issues.

Is this the expected behavior? can some one point out the paragraph on the language spec that would cover this point? If this is not a bug at the very least it seem rather anti-intuitive and non-productive; perhaps is done for the sake of back-comparability?.

Valentin Ruano
  • 2,726
  • 19
  • 29
  • 6
    When a generic type is used as a raw type, it loses all it's generics, not just the ones which depend on the type you didn't give. – Peter Lawrey Mar 28 '17 at 18:09
  • @PeterLawrey yes, that seems to be the case but the question is why? – Valentin Ruano Mar 28 '17 at 18:11
  • 1
    I asked one of the developers at the time Java 5.0 was released at it seemed to me there wasn't much resources in place to sort out cases other than, it's either a raw type for backward compatibility or it's a generic. There is no somewhat raw, but still some sane fallback. – Peter Lawrey Mar 28 '17 at 18:14
  • 1
    Why? Why, because the JLS says so, of course! – Lew Bloch Mar 28 '17 at 18:17
  • 1
    Even 11 years ago, it was recognised that rarefied types might be the answer. Maybe they will be in Java 10. – Peter Lawrey Mar 28 '17 at 18:20

1 Answers1

12

The JLS bit that says this (a bit unclearly) is in JLS 4.8:

The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.

So, since rawAlpha is a raw type, the type of rawAlpha.intList is the erasure of List<Integer> intList(). That erasure is List intList().

As for why, I don't have a citation handy, but raw types are only really in Java for backwards compatibility. That means they only need to work as well as they did before generics; what you're asking for is for code that works just a little bit better that it used to. It's not unreasonable, but it's not what they decided on. :-)

yshavit
  • 42,327
  • 7
  • 87
  • 124
  • mmm... "is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C" ... I would say that the text is kind of open to interpretation... so C is here ```Alpha``` right? thus text would read "the type of ```Alpha.intList``` is the raw type that correspond to the erasure of ```List``` in the generic declaration corresponding to ```Alpha``` so ```Alpha```... If it was a method declared as returning ```List tList()``` the sentence makes total sense but ```List```? why erasuring ```T``` would affect ```Integer``` here? – Valentin Ruano Mar 28 '17 at 18:32
  • 2
    @ValentinRuano because the erasure of `List` is `List` -- erasure means _no_ generic information is preserved, not just that the `T` is erased. That's the same reason you can't ask if `foo instanceof List`. – yshavit Mar 28 '17 at 18:34
  • I guess you are right. Is just that to me is not clear what this last part means: "in the generic declaration corresponding to C". It seem to me that if you are right (99.9% probably if you will), then that part of the sentence could have simply be omitted. So it does no suffice to say that the erasure of ```List``` is ```List``` but you need to say that the erasure of ```List``` is ```List``` in the generic declaration corresponding to ```Alpha``` so ```Alpha``` whatever that means, that again sounds rather unclear to me. – Valentin Ruano Mar 28 '17 at 18:44
  • 1
    JLS language takes some getting used to, but it's very objective and not at all "subject to interpretation". It states very precisely that if a raw type is used, all its members will be treated as being of raw types also. That's why you got the result you got, @ValentinRuano. – Lew Bloch Mar 28 '17 at 21:26