Подтвердить что ты не робот

Почему эта ссылка на метод Java 8 компилируется?

В настоящее время я погружаюсь глубже в функции Java 8, такие как Lambdas и ссылки на методы. Играя немного, я привел меня к следующему примеру:

public class ConsumerTest {

  private static final String[] NAMES = {"Tony", "Bruce", "Steve", "Thor"};

   public static void main(String[] args) {
      Arrays.asList(NAMES).forEach(Objects::requireNonNull);
   }
}

Мой вопрос:

Почему строка внутри основного метода компилируется?

Если я правильно понял это, сигнатура метода ссылки должна соответствовать сигнатуре SAM функционального интерфейса. В этом случае потребитель требует следующую подпись:

void accept(T t);

Однако метод requireNonNull возвращает T вместо void:

public static <T> T requireNonNull(T obj)
4b9b3361

Ответ 1

Спецификация языка Java версии 8 в 15.13.2:

Ссылочное выражение метода совместимо в контексте назначения, контексте вызова или контексте каста с целевым типом T, если T - тип функционального интерфейса (§9.8), и это выражение согласуется с типом функции основного целевого типа, полученного из T.

[..]

Ссылочное выражение метода сравнимо с типом функции, если выполняются оба значения:

  • Тип функции определяет одно объявление времени компиляции, соответствующее ссылке.
  • Верно одно из следующих утверждений:
    • Результат типа функции недействителен.
    • Результатом типа функции является R, и результат применения преобразования захвата (§5.1.10) к типу возврата тип вызова (§15.12.2.6) выбранной декларации времени компиляции - это R '(где R - целевой тип, который может использоваться для вывода R '), и ни R, ни R' недействительны, а R 'совместим с R в контексте назначения.

(акцент мой)

Итак, тот факт, что результат типа функции недействителен, достаточно, чтобы он позволял ему соответствовать.

JLS 15.12.2.5 также специально упоминает использование void при сопоставлении методов. Для лямбда-выражения существует понятие блока, совместимого с void (15.27.2), на который ссылается 15.12.2.1, но для ссылок на методы нет эквивалентного определения.

Мне не удалось найти более подробное объяснение (но JLS - это жесткая гайка для взлома, поэтому, возможно, мне не хватает некоторых соответствующих разделов), но я полагаю, что это связано с тем, что вам также разрешено для вызова непустых методов как самостоятельного утверждения (без назначения, return и т.д.)).

JLS 15.13.3 Оценка времени выполнения ссылок на методы также гласит:

Для определения результата времени компиляции выражение вызова метода является выражением выражения, если результат метода вызова недействителен, а выражение выражения return, если результат метода вызова не является void.

Эффект этого определения, когда объявление времени компиляции ссылки на метод является полиморфным сигнатурой, заключается в следующем:

  • Типы параметров для вызова метода - это типы соответствующих аргументов.
  • Вызов метода либо недействителен, либо имеет возвращаемый тип объекта, в зависимости от того, является ли метод вызова, который заключает вызов метода, недействительным или имеет тип возврата.

Таким образом, сгенерированный вызов метода будет недействительным для соответствия функциональному типу.

Ответ 2

Помимо того, что он говорит в точном ответе @Mark Rotteveel, он компилируется, потому что в Java вы можете игнорировать результат любого вызова метода, например, в следующем примере:

Map<String, String> map = new HashMap<>();

map.put("1", "a");

map.put("1", "A"); // Who cares about the returned value "a"?

Поскольку вы не возвращаете ничего в потребительский блок forEach(), тогда он действителен в соответствии со спецификацией.