Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8328617

Javac fails to select overloaded method when some but not all use generic types

    XMLWordPrintable

Details

    • 8
    • generic
    • generic

    Description

      ADDITIONAL SYSTEM INFORMATION :
      Works in:
      * oraclejdk1.7.0_40
      Fails in:
      * oraclejdk1.8.0_77
      * oraclejdk1.8.0_301
      * openjdk-11.0.2
      * openjdk-17.0.1
      * temurinjdk-21.0.2+13
      * openjdk-23-ea+14

      A DESCRIPTION OF THE PROBLEM :
      The following Code compiles in Javac 7 and Eclipsec, but not in Javac 8 or higher:

      ```java
      public class Reproducer {
        public static void main(String[] args) {
          B<?> instance = new B<>();
          m(instance);
        }

        static <G> void m(A<G> instance) {
          System.out.println(instance);
        }

        static void m(B<?> instance) {
          System.out.println(instance);
        }

        interface A<G> {}
        static class B<G> implements A<G> {}
      }
      ```

      The compiler error:
      ```
      Reproducer.java:4: error: reference to m is ambiguous
          m(instance);
          ^
        both method <G>m(A<G>) in Reproducer and method m(B<?>) in Reproducer match
        where G is a type-variable:
          G extends Object declared in method <G>m(A<G>)
      1 error
      ```

      If you remove the method `m(B<?>)` then the code compiles and a type of `B<?>` is passed to the method `m(A<G>)`.
      So if you consider the informal intuition given in JLS 15.12.2.5. Choosing the Most Specific Method (https://docs.oracle.com/javase/specs/jls/se21/html/jls-15.html#jls-15.12.2.5) then this code should clearly compile:
      > The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time error.

      Also if you change `m(B<?>)` to the following then the code also compiles:
      ```java
        static <G> void m(B<G> instance) {
          System.out.println(instance);
        }
      ```
      From my understanding of JLS 18.5.4. More Specific Method Inference (https://docs.oracle.com/javase/specs/jls/se21/html/jls-18.html#jls-18.5.4) the methods `m(B<?>)` and `m(B<G>)` should be identical for the purpose of overload resolution. The process is indifferent to the more specific method m1 being generic. It is even made clear that:
      > Even if m1 is generic, the type parameters of m1 are treated as type variables, not inference variables.

      This may be related to https://bugs.openjdk.org/browse/JDK-8325859, but that bug does not appear to be present in Javac 8

      Note that I set "Previously worked in the Release" to "8 update releases", even though this issue is present in Javac 8. That is because I was not allowed to select Java 7, but still wanted to make sure this is recognized as a regression.

      REGRESSION : Last worked in version 8

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      echo "public class Reproducer {
        public static void main(String[] args) {
          B<?> instance = new B<>();
          m(instance);
        }

        static <G> void m(A<G> instance) {
          System.out.println(instance);
        }

        static void m(B<?> instance) {
          System.out.println(instance);
        }

        interface A<G> {}

        static class B<G> implements A<G> {}
      }
      " > Reproducer.java

      javac Reproducer.java

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The code compiles and the main method invokes the overload `m(B<?>)`.
      ACTUAL -
      Reproducer.java:4: error: reference to m is ambiguous
          m(instance);
          ^
        both method <G>m(A<G>) in Reproducer and method m(B<?>) in Reproducer match
        where G is a type-variable:
          G extends Object declared in method <G>m(A<G>)
      1 error

      CUSTOMER SUBMITTED WORKAROUND :
      Use a generic type parameter instead of a wildcard. For example instead of
      ```java
        static void m(B<?> instance) {
          System.out.println(instance);
        }
      ```
      you can write the following to bypass the error:
      ```java
        static <G> void m(B<G> instance) {
          System.out.println(instance);
        }
      ```

      FREQUENCY : always


      Attachments

        Activity

          People

            vromero Vicente Arturo Romero Zaldivar
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated: