Ary Borenszweig

Ary Borenszweig

Why Java generics don’t have problems with right shift operator?

Oct 12 2008
3 min

You know that in C++ a template instatiation like:

Foo<Bar<Baz>>

is problematic to parse. Nevertheless, Java seems to repeat history and chosed the same simbols for it’s generics, but nobody seems to complain about it. Do you happen to know why? I sat for a moment to think about it, consulted the Java Language Specification, and it turns out a Type is defined as follows:

Type:
        Identifier [TypeArguments]{ . Identifier [TypeArguments]} {[]}
        BasicType

TypeArguments:
        < TypeArgument {, TypeArgument} >

TypeArgument:
        Type
        ? [( extends |super ) Type]

Note that as a TypeArgument, only another Type is allowed. This gives us the clue about why this problem is much easier to solve in C++: arguments to generic types are always types, never expressions.

So if a C++ parser already read

Foo<Bar<Baz>>

it may be that Baz resolves to an integer, that Bar is a template that receives an integer as an argument, and so the parser doesn’t know if Baz is the argument of Bar, or if Baz >> somethingElse (which the parser didn’t already read, that is, Baz right shift something else) is it’s argument.

In Java, if the parser is in the same situation, by simply using a stack of previously encountered generic types, this is very easy to solve. Debugging JDT’s code I found these lines:

// when consuming a rule...
case 538:
  if (DEBUG) {
    System.out.println(
      "ReferenceType2 ::= ReferenceType RIGHT_SHIFT");
  }
  consumeReferenceType2();
  break;
case 544:
  if (DEBUG) {
    System.out.println(
      "ReferenceType3 ::= ReferenceType UNSIGNED_RIGHT_SHIFT");
  }
  consumeReferenceType3();
  break;

As you can see, the case where a type is encountered followed by a right shift is built in the parser, and the consumeReferenceTypeN() methods simply check their generic types stack to see if everything is ok.