Java 8 generics The method ... is not applicable for the arguments in Eclipse
Asked Answered
G

1

6

During the migration of our code base from java 1.7 to 1.8 we’ve got an error message "The method ... is not applicable for the arguments" on several code locations, all following the same pattern in generics usage.

We are currently using mostly Eclipse Mars (4.5.2) on Windows 7, but could confirm the behavior with Neon (4.6), too. Javac as well as ecj with 1.7 compliance level can both compile our code without an error.

Here is a Minimal, Complete, and Verifiable example:

public class ComplexInterfaceTest {

  public static class Foo {}

  public interface Bar {
    void print();
  }

  public static class SubFooBar extends Foo implements Bar {
    public void print() {
      System.out.println(this.getClass().getSimpleName());
    }
  }

  public static class FooBar<T extends Foo & Bar> {
    public static <T extends Foo & Bar> FooBar<T> makeFooBar() {
      return new FooBar<>();
    }

    public void create(T value) {
      value.print();
      return;
    }
  }

  public static class Base<T extends Foo> {}

  public static class Subclass extends Base<SubFooBar> {
    public void doSomething(SubFooBar value) {
//      FooBar.<SubFooBar>makeFooBar().create(value);
      FooBar.makeFooBar().create(value);
    }
  }

  public static void main(String[] args) {
    new Subclass().doSomething(new SubFooBar());
  }

}

Now switching the commented out lines in doSomething method makes the code to compile, so we do have a workaround. Still the error message seems not right, as the class SubFooBar extend Foo and implements Bar, so it fulfills the contract of <T extends Foo & Bar>, which is required in <T extends Foo & Bar> FooBar<T> makeFooBar(), so actually T IMO should be bound to SubFooBar.

I searched for similar questions and found these: Differences in type inference JDK8 javac/Eclipse Luna? Type Inference Compiler Error In Eclipse with Java8 but not with Java7

Which makes me think it might be an ecj bug. In this course I also looked into Eclipse Bugzilla but couldn’t find anything comparable, I saw these:

  • 430686 is verified fixed - mine is not
  • 440019 has to do with scalar type - mine not
  • 492838, 448793 have to do with wildcards – mine not

Now Eclipse Bugzilla discussions are full of details about internal workings of the ecj, which I not always can follow. What I understand though is the general consensus there, that Eclipse compiler has to strictly follow JLS and not javac (in cases where it were wrong), so it doesn’t necessarily must be a bug in ecj. If it weren’t an ecj bug, then compiling the code must have been a javac bug.

What I am interested in is – for those that can analyze the type inference process of my code snippet - should the code have compiled or did I make error in coding?

EDIT

As I promised to post the outcome of my report to Eclipse’s Bugzilla: the defect has the ID #497905 (Stephan Herrmann has posted the link in his comment below the accepted answer) and is currently targeted for v4.7.

Giro answered 14/7, 2016 at 12:37 Comment(1)
I have just posted a bug report on Eclipse Bugzilla, will post here their answers.Giro
D
2

In the method

public void doSomething(SubFooBar value) {
  FooBar.makeFooBar().create(value);
}

the type parameter T of the method makeFooBar() will never be inferred as SubFooBar. The fact that you are going to pass an instance of SubFooBar to the create method afterwards, does not influence the type of the preceding invocation expression FooBar.makeFooBar().

This doesn’t change with Java 8’s target typing, as this new feature doesn’t work on the receiver expression of a chained method invocation.

So in all versions, the type inferred for T of the makeFooBar() invocation will be the intersection type Foo & Bar, so the result type is FooBar<Foo&Bar>. This is also what Eclipse infers, even if the tooltip in the editor may display something else.

This implies that you can pass a SubFooBar instance to the create method as FooBar<Foo&Bar>.create(…) expects an instance of Foo&Bar and SubFooBar extending Foo and implementing Bar is compatible.

It’s possible to demonstrate that Eclipse does infer the same type as all other compilers, as inserting an appropriate type cast

public void doSomething(SubFooBar value) {
  FooBar.makeFooBar().create((Foo&Bar)value);
}

makes the compiler error go away. So the problem here is not the type inference, but that this Eclipse version thinks that SubFooBar is not assignable to Foo & Bar.

Danelledanete answered 14/7, 2016 at 14:58 Comment(3)
yep, this matches my analysis as well (bugs.eclipse.org/bugs/show_bug.cgi?id=497905#c1)Macadam
So basically since T should be Foo&Bar and SubFooBar is Foo&Bar the code should have compiled. Good to know, thanks!Giro
I have let it some time to sink in – I think there is not much that can be added: even though I missed the correct reason the point is that my code seems to be correct, so the issue will be targeted for the Eclipse version 4.7.Giro

© 2022 - 2024 — McMap. All rights reserved.