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

Выбор между перегруженными методами, если фактическим параметром является лямбда

В Java 1.8 следующее лямбда-выражение соответствует как функциональным интерфейсам Runnable, так и Callable:

() -> {
    throw new RuntimeException("FIXME");
}

Тем не менее, если я отправлю его ExecutorService с использованием метода с одним аргументом и игнорирует возвращаемое значение (т.е. информация о типе типа недоступна) ExecutorService#submit(Callable) выбирается во время компиляции, если я явно не включил lambda в Runnable.

Как компилятор выбирает между перегруженными методами в приведенном выше случае, при условии, что Runnable и Callable не имеют общей иерархии, а правило определенного типа не применяется здесь?

4b9b3361

Ответ 1

Я считаю, что это потому, что Callable объявляет тип возврата, а Runnable не работает.

В разделе JLS 15.12.2.5 выбрана перегрузка с наиболее конкретным типом, если есть один однозначно самый конкретный. Это то, что он говорит о наиболее конкретных типах функциональных интерфейсов:

Тип функционального интерфейса S более специфичен, чем тип функционального интерфейса T для выражения e, если T не является подтипом S, и одно из следующего верно (где U1... Uk и R1 - типы параметров и возвращаемый тип типа функции захвата S, а V1... Vk и R2 - типы параметров и возвращаемый тип типа функции T):

Если e - явно типизированное лямбда-выражение (§15.27.1), то выполняется одно из следующих утверждений:

  • R2 недействителен...

T Runnable, S is Callable, Callable более конкретный, потому что его возвращаемый тип не является недействительным, поэтому Callable выбран

Разрешение перегрузки метода очень сложное, поэтому может быть бит, который я пропустил, но я думаю, именно поэтому он выбирает Callable

Ответ 2

Хотя ответ @thecoop верен, есть еще один аспект лямбда-механизма, который стоит упомянуть.

Lambdas с блочными телами делятся на две категории: совместимость с void и ценность.

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

Большинство лямбдов, однако, либо одно, либо другое. И если лямбда только void-compatible, тогда лямбда будет совместима только с Runnable.

Что является слегка контр-интуитивным (но логически правильным), так это то, что хотя лямбда в вопросе никогда не возвращает значение, он классифицируется как "всегда возвращающее значение, когда есть return".