Когда синтаксис алмаза не работает в Java 8?

Как и в случае с Java 7, синтаксис алмаза не всегда срабатывал в аргументах метода, например. Почему оператор алмаза не работает для java.util.Collections методов в Java 7?. Ответ на этот вопрос говорит о том, что вывод целевого типа в Java 8 устраняет эту проблему.

Существуют ли какие-либо оставшиеся случаи, когда синтаксис алмаза нельзя использовать?

Ответы

Ответ 1

Алмазный оператор не всегда может использоваться в Java 8. Первоначальный план улучшения вывода в Java 8 (JEP 101) имел два цели:

  • Добавить поддержку вывода метода-типа в контексте метода
  • Добавить поддержку вывода типа метода в цепочку вызовов

Выполнено только первое. Заимствуя пример из JEP, рассмотрим следующий класс:

class List<E> {
   static <Z> List<Z> cons(Z head, List<Z> tail) { ... };
   E head() { ... }
}

В Java 8 улучшенный вывод контекста метода позволяет компилировать следующее. С Java 7 он потерпит неудачу с ошибкой expected List<Integer>, found List<Object>

List<Integer> l = List.cons(42, new List<>());

Однако примеры, требующие вывода цепочных вызовов, по-прежнему не работают с Java 8:

Integer i = new List<>().head();

Раздел D JSR 335 включает в себя подсказку о том, почему принудительный вывод выражения был оставлен для Java 8:

Была некоторая заинтересованность в разрешении вывода "chain": in a(). b(), передавая информацию о типе от вызова b к вызову a. Это добавляет еще одно измерение сложности алгоритма вывода, поскольку частичная информация должна проходить в обоих направлениях; он работает только тогда, когда стирание возвращаемого типа a() фиксировано для всех экземпляров (например, List). Эта особенность не очень хорошо вписывается в модель выражения poly, так как тип цели не может быть легко получен; но, возможно, с дополнительными улучшениями, которые могут быть добавлены в будущем.


Есть еще несколько надуманных примеров, когда алмаз нельзя использовать.

Если подсчет ошибок, это не компилируется с javac до jdk8u25. (см. JDK-8029002)

class Test {
  class C<T extends C<T>> {}
  void m() {
    C<?> i = new C<>();
  }
}

error: incompatible types: cannot infer type arguments for Test.C<>
    C<?> i = new C<>();
                  ^
    reason: inferred type does not conform to upper bound(s)
      inferred: Test.C<CAP#1>
      upper bound(s): Test.C<Test.C<CAP#1>>,Test.C<CAP#1>
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Test.C<CAP#1> from capture of ?

Также существует проблема с производительностью (JDK-8051946) с новой реализацией вывода типа, которая может повлиять на код с использованием оператора алмаза. Следующий пример требует минут для компиляции, если используется оператор алмаза.

class Test {
  <T> T and(T a, T b) { return null; }
  class C<T> {}

  void g(String s) {}
  void g(Object s) {}

  void m() {
    g(
        and(
        and(
        and(
        and(
        and(
        and(
        and(new C<>(),
            new C<>()),
            new C<>()),
            new C<>()),
            new C<>()),
            new C<>()),
            new C<>()),
            new C<>()));
  }
}