Необязательная карта и orElse должны возвращать тот же тип - PullRequest
0 голосов
/ 27 февраля 2020

У меня есть этот код:

Object s = ofNullable(resp).map(ResponseEntity::getBody).orElse(ex.getMessage());
ResponseEntity::getBody returns Object and ex.getMessage returns String

Я получаю ошибку компилятора:

The method orElse(capture#10-of ?) in the type Optional<capture#10-of ?> is not applicable for the arguments (String)

Но я могу написать это без опционально, и она отлично работает:

Object s = resp != null ? resp.getBody() : ex.getMessage();

1 Ответ

2 голосов
/ 27 февраля 2020

Проблема связана с тем, как Java делает вывод типа.

Ссылки на методы (и лямбда-выражения) являются поли-выражениями . Это означает, что одно и то же выражение может иметь различный тип, в зависимости от того, где оно используется.

Итак, ваше полиэкспрессия:

ofNullable(resp).map(obj1::get1)

имеет некоторый тип.

Если бы вы использовали его в контексте присваивания:

Optional<Object> opt1 = ofNullable(resp).map(obj1::get1);
Optional<?> opt2      = ofNullable(resp).map(obj1::get1);

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

Однако, если вы используете выражение в качестве получателя вызова другого метода:

ofNullable(resp).map(obj1::get1).orElse(...)

, тогда Java сначала определяет тип ofNullable(...).map(...) (больше не полиэкспрессия), а затем переходит к orElse.

. Очевидно, что obj1.get1(resp) возвращает не Object, как вы утверждаете, а какой-то подстановочный тип. Это предполагаемый тип необязательного: Optional<some wildcard>. Тогда параметр orElse также должен иметь тип some wildcard; единственное возможное значение для этого - null.

. Существует несколько способов обойти это:

  • Вы можете явно присвоить результат ofNullable(...).map(...) переменной типа Optional<Object>, затем вызовите orElse для переменной;

  • Вы можете привести к Optional<Object>:

    ((Optional<Object>) ofNullable(...).map(...)).orElse(...)
    
  • Вы можете явно сопоставить объекту:

    ofNullable(...).map(...).map(Object.class::cast)
    
  • Вместо этого можно использовать лямбду и привести результат к Object:

    ofNullable(...).map(r -> (Object) ...).orElse(...)
    

Или вы можете просто сделать это проще и не пытаться использовать Optional:

Object s = (resp != null) ? obj1.get(resp) : ex.getMessage();
...