Выбор между перегруженными методами, если фактическим параметром является лямбда
В Java 1.8 следующее лямбда-выражение соответствует как функциональным интерфейсам Runnable
, так и Callable
:
() -> {
throw new RuntimeException("FIXME");
}
Тем не менее, если я отправлю его ExecutorService
с использованием метода с одним аргументом и игнорирует возвращаемое значение (т.е. информация о типе типа недоступна) ExecutorService#submit(Callable)
выбирается во время компиляции, если я явно не включил lambda в Runnable
.
Как компилятор выбирает между перегруженными методами в приведенном выше случае, при условии, что Runnable
и Callable
не имеют общей иерархии, а правило определенного типа не применяется здесь?
Ответы
Ответ 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), то выполняется одно из следующих утверждений:
T
Runnable
, S
is Callable
, Callable
более конкретный, потому что его возвращаемый тип не является недействительным, поэтому Callable
выбран
Разрешение перегрузки метода очень сложное, поэтому может быть бит, который я пропустил, но я думаю, именно поэтому он выбирает Callable
Ответ 2
Хотя ответ @thecoop верен, есть еще один аспект лямбда-механизма, который стоит упомянуть.
Lambdas с блочными телами делятся на две категории: совместимость с void и ценность.
Лямбда в вопросе - это то, что он не может нормально завершиться (из-за безусловного исключения), и все операторы return
в них не имеют значения и возвращают значение, видя, что нет инструкции return
в все.
Большинство лямбдов, однако, либо одно, либо другое. И если лямбда только void-compatible
, тогда лямбда будет совместима только с Runnable
.
Что является слегка контр-интуитивным (но логически правильным), так это то, что хотя лямбда в вопросе никогда не возвращает значение, он классифицируется как "всегда возвращающее значение, когда есть return
".