12

The Java Language Specification for Java 8 provides an example of a method call with a type argument in "Example 4.11-1. Usage of a Type":

<S> void loop(S s) {
    this.<S>loop(s); // <S> is the the type argument for the method call.
}

In that example the supplied type argument is meaningful, but apparently type arguments for method calls can also be redundant and completely meaningless, and generics need not even be involved. For example:

void m() { }

void test() {
    m(); 
    this.m(); 
    this.<Integer>m(); // Compiles and runs OK!
    this.<String, Byte, StringBuilder, Thread[], Throwable>m(); // Compiles and runs OK!
    <Integer>m(); // Won't compile: "illegal start of expression"
}

I have a couple of questions arising:

  1. Can anyone suggest a valid reason for Java allowing these redundant type parameters? Accepting that they do no harm, it still seems to me like something that the compiler could and should catch.

  2. That code only compiles if the method calls with type arguments are prefixed with "this.". Otherwise you get "illegal start of expression" errors. Is that a bug? Shouldn't any unambiguous method call that works with "this." also work without "this."?

(The catalyst for these questions is Oracle's response to a bug report I created for an interesting Java problem someone raised here on SO.)

UPDATE Sep 18, 2015

  1. I raised bug JDK-8098556 for this issue with Oracle. Here is their response:

This is not an issue; method references are checked using same rules as plain methods - note that for ordinary methods you can always supply redundant type-arguments:

void m() { }
this.<String>m(); //legal

Method (and constructor) references simply inherit this behavior, as per 15.13: ""If the method reference expression has the form ReferenceType :: [TypeArguments] Identifier, the potentially applicable methods are the member methods of the type to search that have an appropriate name (given by Identifier), accessibility, arity (n or n-1), and type argument arity (derived from [TypeArguments]), as specified in §15.12.2.1."

  1. Since that response confirms the information TAsk had already provided below (including citing the relevant section of JLS), I have accepted that answer.
Community
  • 1
  • 1
skomisa
  • 16,436
  • 7
  • 61
  • 102
  • 1
    Don't know why it's allowed but in Eclipse you get a warning "Unused type arguments for the non generic method m() of type Test; it should not be parameterized with arguments ", which means *don't do it*. --- `this.m()` is *syntactically* valid and has meaning if `m` is typed, but `m()` is plain invalid syntax. – Andreas Aug 28 '15 at 01:11
  • 1
    see http://stackoverflow.com/questions/28014853/what-is-the-point-of-allowing-type-witnesses-on-all-method-calls – ZhongYu Aug 28 '15 at 02:30
  • the 2nd question must be because some parsing/grammar difficulties. for example, `x<m()` – ZhongYu Aug 28 '15 at 02:42
  • @bayou.io Your link is very relevant. The answer given there by Gábor Bakos as to why the "invalid" use of type parameters is tolerated is somewhat persuasive, even though it is addressing a pretty rare scenario. – skomisa Aug 28 '15 at 03:19
  • @skomisa - I think Bakos' answer is completely unfounded. There has never been a goal of comparability with un-generification. – ZhongYu Aug 28 '15 at 03:24
  • @bayou.io You may well be right that it is unfounded, but I haven't seen a better explanation being offered. From looking at the JLS, evaluating method calls is a surprisingly complex business, and I'm guessing that the designers decided to be over-tolerant of edge cases rather than intolerant, but that's just a guess. I know nothing of what specific goals they had in mind. – skomisa Aug 28 '15 at 03:59
  • These discussions happened a long time ago, before 2005. It is too hard to find out exactly what they were thinking. But, we don't need to presume that they made the right, or even reasonable, choices. – ZhongYu Aug 28 '15 at 04:08

1 Answers1

5

Following are the ways of method invocation :

JLS 15.12 lists following ways for invocation of method,

  • MethodName ( [ArgumentList] ) (Note : This does not have type argument)
  • TypeName.[TypeArguments] Identifier ( [ArgumentList] )
  • ExpressionName.[TypeArguments] Identifier ( [ArgumentList] )
  • Primary.[TypeArguments] Identifier ( [ArgumentList] )
  • super.[TypeArguments] Identifier ( [ArgumentList] )
  • TypeName.super.[TypeArguments] Identifier ( [ArgumentList] )

So, it is there in Java language specification that method with expression or type name can have type arguments but note that in first method call you can not specify type argument as it's illegal.

Note that only this does not have allowed this but static call and super method calls can also have type arguments and those are totally legal.

static void m() { }

void test() {
   ClassName.<Integer>m();//Also compiles
}

Other than that you will have following warning,

Unused type arguments for the non generic method m() ...

Which stands for following statement of JLS 15.12.2.1,

This clause implies that a non-generic method may be potentially applicable to an invocation that supplies explicit type arguments. Indeed, it may turn out to be applicable. In such a case, the type arguments will simply be ignored.

It says Indeed, it may turn out to be applicable (at run time)

Moreover,

This rule stems from issues of compatibility and principles of substitutability. Since interfaces or superclasses may be generified independently of their subtypes, we may override a generic method with a non-generic one. However, the overriding (non-generic) method must be applicable to calls to the generic method, including calls that explicitly pass type arguments. Otherwise the subtype would not be substitutable for its generified supertype.

Community
  • 1
  • 1
akash
  • 22,664
  • 11
  • 59
  • 87
  • 1
    Your quote from JLS15.12.2.1 certainly nails why the type parameters are tolerated/ignored, though it seems a stretch for that to be relevant in a scenario where there is no use of generics, and the called method has no parameters. Andreas pointed out in a comment above that Eclipse at least gives a compiler warning. I was using Oracle JDK 8u60, which gives no warning at all. – skomisa Aug 28 '15 at 03:05