Ary Borenszweig

Ary Borenszweig

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

3 min
Oct 12 2008
coding
3 min
Oct 12 2008

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.