etxalpo
etxalpo

Reputation: 1176

Java code fails in Intellij but not in eclipse, with ambiguous method error

I have the below Java class (with nested classes/interfaces). When running the main method from within Eclipse (Version: 2019-09 R (4.13.0)) I get the following output:

java.version: 1.8.0_241
PageA.m3() called 

This is the command line used by Eclipse:

/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/bin/java -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:52180 -javaagent:/Users/*****/eclipse/java-2019-09/Eclipse.app/Contents/Eclipse/configuration/org.eclipse.osgi/222/0/.cp/lib/javaagent-shaded.jar -Dfile.encoding=UTF-8 -classpath <a-list-of-.jar-files> _play.PageTest

When running the same code from within Intellij (IDEA 2019.3.3 (Community Edition)) I get the following output:

src/main/java/_play/PageTest.java:13: error: reference to m1 is ambiguous
                .m1(pageAorB -> ((SpecialPage<?>) pageAorB).m3());
                ^
  both method m1(Consumer<T#1>) in Page and method m1(Consumer<T#2>) in AbstractPage match
  where T#1,T#2 are type-variables:
    T#1 extends Page<T#1> declared in interface Page
    T#2 extends Page<T#2> declared in class AbstractPage

Why do I get this error in Intellij but not in Eclipse? Is there a way to solve this so it runs without error in Intellij?

Here is the Java class:

package _play;

import java.util.function.Consumer;
import java.util.function.Function;

public class PageTest {

    public static void main(String[] args) {
        System.out.println("java.version: " + System.getProperty("java.version"));

        new PageC()
                .m2(pageC -> (1 == 1 ? new PageA() : new PageB()))
                .m1(pageAorB -> ((SpecialPage<?>) pageAorB).m3());
    }

    public static interface Page<T extends Page<T>> {
        T m1(Consumer<T> c);
        <R> R m2(Function<? super T, ? extends R> f);
    }

    public static abstract class AbstractPage<T extends Page<T>> implements Page<T>{
        @Override
        public T m1(Consumer<T> c){
            c.accept(self());
            return self();
        }

        @Override
        public final <R> R m2(Function<? super T, ? extends R> f) {
            return f.apply(self());
        }

        abstract protected T self();
    }

    public static interface SpecialPage<T extends Page<T>> extends Page<T> {
        T m3();
    }

    public static class PageC extends AbstractPage<PageC> {

        @Override
        protected PageC self() {
            return this;
        }
    }

    public static class PageB extends AbstractPage<PageB> implements SpecialPage<PageB> {
        @Override
        public PageB m3() {
            System.out.println("PageB.m3() called");
            return this;
        }

        @Override
        public PageB self() {
            return this;
        }
    }

    public static class PageA extends AbstractPage<PageA> implements SpecialPage<PageA> {
        @Override
        public PageA m3() {
            System.out.println("PageA.m3() called");
            return this;
        }

        @Override
        public PageA self() {
            return this;
        }
    }
}

EDIT In this case the Page interface and AbstractPage class are in a library that the client can't change. but the client should be able to extend the Page interface.

Upvotes: 2

Views: 575

Answers (1)

howlger
howlger

Reputation: 34255

The question is, is the Eclipse compiler (ecj) of version 4.13 or javac 1.8.0_241 which is used by IntelliJ and Gradle by default right?

The error message of javac says it's ambiguous because in AbstractPage the type variable T refers to the type variable of AbstractPage (named T) and also refers to the different type variable of Page (also named T). But in fact, both type variable refer to the same type, not because they are named the same, but because AbstractPage implements Page<T>. It is not ambiguous and javac erroneously gives the compile error here.

As a workaround of this javac bug you can do one of the following:

  • Use the Eclipse compiler also in IntelliJ and Gradle
  • Rewrite the code in the main method using a variable of SpecialPage<? extends Page<?>> for the intermediate result so that javac need not infer it:

    SpecialPage<? extends Page<?>> pageAorB = new PageC().m2(pageC -> (1 == 1 ? new PageA() : new PageB()));
    pageAorB.m1(specialPage -> ((SpecialPage<?>) specialPage).m3());
    

Upvotes: 1

Related Questions