Обобщения Java 8 Метод... не применим для аргументов в Eclipse

Во время миграции нашей кодовой базы с Java 1.7 на 1.8 мы получили сообщение об ошибке "Метод... не применим для аргументов" в нескольких местах кода, все по той же схеме при использовании обобщенных типов.

В настоящее время мы используем в основном Eclipse Mars (4.5.2) на Windows 7, но может подтвердить поведение с Neon (4.6) тоже. Javac так же как ecj с уровнем соответствия 1.7 можно скомпилировать наш код без ошибок.

Вот минимальный, полный и проверяемый пример:

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());
  }

}

Теперь переключаем закомментированные строки в doSomething Метод делает код для компиляции, поэтому у нас есть обходной путь. Тем не менее сообщение об ошибке кажется не правильным, как класс SubFooBar простираться Foo и реализует Bar, так что выполняет контракт <T extends Foo & Bar>, который требуется в <T extends Foo & Bar> FooBar<T> makeFooBar()так на самом деле T ИМО должен быть связан с SubFooBar,

Я искал похожие вопросы и нашел их: Различия в выводе типов JDK8 javac/Eclipse Luna? Ошибка компиляции вывода типа в Eclipse с Java8, но не с Java7

Что заставляет меня думать, что это может быть ecj ошибка. В этом курсе я также изучил Eclipse Bugzilla но не смог найти ничего похожего, я видел это:

  • 430686 подтверждено исправлено - у меня нет
  • 440019 имеет отношение к скалярному типу - мой не
  • 492838, 448793 имеют отношение к групповым символам - мой не

Сейчас Eclipse Bugzilla обсуждения полны деталей о внутренней работе ecjЗа которым я не всегда могу следить. Что я понимаю, хотя это общее согласие там, что Eclipse компилятор должен строго следовать JLS и не javac (в случаях, когда это было неправильно), поэтому это не обязательно должно быть ошибкой в ecj, Если бы это не было ecj ошибка, то компиляция кода должна была javac ошибка.

Что меня интересует - для тех, кто может анализировать процесс вывода типа моего фрагмента кода - должен ли код скомпилироваться или я допустил ошибку в кодировании?

РЕДАКТИРОВАТЬ

Как я и обещал опубликовать результаты моего доклада в Eclipse's Bugzilla: дефект имеет идентификатор #497905 (Стефан Херрманн разместил ссылку в своем комментарии под принятым ответом) и в настоящее время предназначен для версии 4.7.

1 ответ

Решение

В методе

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

параметр типа T метода makeFooBar() никогда не будет выводиться как SubFooBar, Тот факт, что вы собираетесь передать экземпляр SubFooBar к create метод впоследствии, не влияет на тип предыдущего выражения вызова FooBar.makeFooBar(),

Это не меняется с типизацией цели Java 8, так как эта новая функция не работает с выражением-получателем при вызове цепочечного метода.

Таким образом, во всех версиях, тип выведен для T из makeFooBar() вызов будет типом пересечения Foo & Barпоэтому тип результата FooBar<Foo&Bar>, Это также то, что выводит Eclipse, даже если подсказка в редакторе может отображать что-то еще.

Это означает, что вы можете передать SubFooBar экземпляр к create метод как FooBar<Foo&Bar>.create(…) ожидает случай Foo&Bar а также SubFooBar простирающийся Foo и внедрение Bar совместимо

Можно продемонстрировать, что Eclipse выводит тот же тип, что и все другие компиляторы, как вставка соответствующего приведения типа

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

делает ошибку компилятора исчезающей Так что проблема здесь не в выводе типа, а в том, что эта версия Eclipse считает, что SubFooBar не присваивается Foo & Bar,

Другие вопросы по тегам