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

151
19

В настоящее время я погружаюсь глубже в функции 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)

спросил(а) 2015-09-12T15:58:00+03:00 5 лет назад
1
Решение
148

Спецификация языка 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.

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

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

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

ответил(а) 2015-09-12T16:09:00+03:00 5 лет назад
81

Помимо того, что он говорит в точном ответе @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(), тогда он действителен в соответствии со спецификацией.

ответил(а) 2015-09-12T16:47:00+03:00 5 лет назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема