7

I'm using Java 1.8.0_151 and there is some code that doesn't compile and I don't understand:

Optional optional = Optional.of("dummy"); 
Optional<Boolean> result1 = optional.map(obj -> true);     // works fine
boolean result2 = result1.orElse(false);                   // works fine
boolean result3 = optional.map(obj -> true).orElse(false); // compilation error: Incompatible types: required boolean, found object
Object result4 = optional.map(obj -> true).orElse(false); // works fine

why it works fine on result1 but gives compilation error on result3?
Additional info:

  • In the first line, when I change Optional to Optional<String>, result3 is also able to compile
  • When I break result3 into 2 lines: like result1 and result2, result3 is able to compile
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
HenryNguyen
  • 1,153
  • 1
  • 10
  • 8
  • 4
    Possible duplicate of [What is a raw type and why shouldn't we use it?](https://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it) – Andrew Tobilko Nov 20 '17 at 12:59
  • 2
    @AndrewTobilko may be the OP did that *on purpose* – Eugene Nov 20 '17 at 13:00

3 Answers3

9

Once you lose the type safety - it is lost for chained calls as-well. That is Optional<Object> != Optional. So when you do

 Optional optional = Optional.of("dummy");
 optional.map()

The map can only accept a raw Function and nothing else, which will return an Object obviously.

The correct way is to add type information :

Optional<String> optional = Optional.of("dummy");

Or you can cast unsafely:

boolean result3 = (boolean) optional.map(obj -> true).orElse(false)
Eugene
  • 117,005
  • 15
  • 201
  • 306
4

optional is a raw Optional, so optional.map(obj -> true) returns a raw Optional, and orElse(false) returns an Object, not a Boolean. The compiler doesn't know how to un-box an Object to boolean.

By changing

Optional optional = Optional.of("dummy"); 

to

Optional<Object> optional = Optional.of("dummy"); 

or

Optional<String> optional = Optional.of("dummy"); 

you will overcome this error, since now optional.map(obj -> true) will return an Optional<Boolean> and orElse(false) will return a Boolean.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • I afraid `optional.map(obj -> true);` doesn't return a raw `Optional`, but `Optional` because the line: `Optional result1 = optional.map(obj -> true);` works fine – HenryNguyen Nov 20 '17 at 14:44
  • 3
    @Henry actually a raw type can be assigned.to a generic type, so that assignment doesn't mean what you think it does. – Eran Nov 20 '17 at 15:46
0

Thanks for your answers. I just want to give details on why it worked on resutl1 but not result3 on:

Optional<Boolean> result1 = optional.map(obj -> true);     // works fine
boolean result2 = result1.orElse(false);                   // works fine
boolean result3 = optional.map(obj -> true).orElse(false); // compilation error: Incompatible types: required boolean, found object

on result1, the optional variable was raw type, so optional.map(obj -> true) returned an Optional raw type.

When I declared Optional<Boolean> result1, Optional raw type was automatically casted to Optional<Boolean>
On the other hand, optional.map(obj -> true).orElse(false); failed because a raw type Optional object cannot call .orElse(false)

Another example of this behaviour with raw type is:

List li = new ArrayList<String>();
List<String> li1 = li;
List<Integer> li2 = li;
List<Map> li3 = li;

li1.add("WWW");
li2.add(444);
li3.add(new HashMap());

will work fine for all scenarios, the li object will contain a String, an Integer and a HashMap(). The li1, li2, li3 was automatically casted to non-raw type.

HenryNguyen
  • 1,153
  • 1
  • 10
  • 8