JDK-8167000 : Refine handling of multiple maximally specific abstract methods
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 9
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2016-10-01
  • Updated: 2017-05-17
  • Resolved: 2016-10-17
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9 b141Fixed
Related Reports
Relates :  
Description
JDK-7034913 cleaned up the rules for determining return types and throws clauses of method invocations that reference multiple maximally specific abstract methods. javac behavior needs to be reviewed to check if it is aligned with these rules.

Some specific areas to look into:
- Are subtype returns preferred over return-type-substitutable returns?
- Is the most specific method (and the source of the return type) required to have a subsignature of all others?
- Is anything about the algorithm encounter-order dependent?
- If there is no preferred method (as defined), does an ambiguity error occur?
- Are type variables adapted before making comparisons?
- Is there any difference from the function type algorithm (9.9)? Is the code shared?

The tests appearing in JDK-7034913 are probably worth adding to our test suite.
Comments
The only potential difference I see between the described algorithm and the implemented logic is that javac does everything in one round: i.e. if M is the set of maximally specific methods, and 'm' is one method in this set, javac checks that 'm' is a subsignatures of all methods in M and then picks the most specific return type by comparing 'm' return type against all methods in M. The comparison first uses subtyping, then uses RTS as a fallback. Here is a test case that demonstrates the difference in behavior. import java.util.*; interface J {List<Number> getAll(String str);} interface K {Collection<Integer> getAll(String str);} interface L {List getAll(String str);} interface M {Collection getAll(String str);} abstract class E implements J, K, L, M { void test() { getAll(""); //should resolve to List::getAll(String), but gives ambiguity } }
04-10-2016

Here's a failure to adapt type variables: interface A { <E extends Exception> void foo(E arg) throws E; } interface B { <F extends Exception> void foo(F arg) throws F; } abstract class C implements A, B { void test(Exception e) { ((A) this).foo(e); // error (as expected) ((B) this).foo(e); // error (as expected) foo(e); // no error (!) (expected an error) } } [Edit: this was fixed between 9+100 and 9+110.]
01-10-2016