Reputation: 7067
I have been converting some code to use Java 8 features. In the following contrived example
Arrays.asList("1", "2", "3", "cheese", "5").stream().map(line -> {
try {
return Optional.of(Integer.parseInt(line));
} catch (NumberFormatException xep) {
return Optional.empty();
}
}).forEach( v ->
System.out.println(v.orElse(999))
);
(the intention is to parse some strings as ints and replace any unparseable values with 999)
The compiler reports
error: incompatible types: int cannot be converted to CAP#1
System.out.println(v.orElse(999))
where CAP#1 is a fresh type-variable:
CAP#1 extends Object from capture of ? extends Object"
I've tried casting 999 to an Integer or Object with no success.
It seems that the real problem is that the inferred return type of the first lambda is Optional<Object>
and not Optional<Integer>
If I do this
Arrays.asList("1", "2", "3", "cheese", "5").stream().map(line -> {
Optional<Integer> ans;
try {
ans = Optional.of(Integer.parseInt(line));
} catch (NumberFormatException xep) {
ans = Optional.empty();
}
return ans;
}).forEach( v ->
System.out.println(v.orElse(999))
);
it works perfectly, but not quite as elegant. Is there a better way to 'guide' the compiler to the return type that I want?
Upvotes: 4
Views: 1782
Reputation: 298143
As said by assylias, you can fix it by using return Optional.<Integer> empty();
.
However, the big question is why are you using Optional
here at all?
Stream.of("1", "2", "3", "cheese", "5").mapToInt(line -> {
try {
return Integer.parseInt(line);
} catch (NumberFormatException xep) {
return 999;
}
}).forEach(System.out::println);
does the job much simpler when you want to replace the value anyway.
If you want to perform an action for valid values only (the equivalent of Optional.ifPresent(Consumer))
you may consider the rule that you should prefer pre-checks over catching exceptions when you expect invalid values:
Stream.of("1", "2", "3", "cheese", "5")
.filter(Pattern.compile("^[+-]?[0-9]{1,9}$").asPredicate())
.mapToInt(Integer::parseInt)
.forEach(System.out::println);
(I simplified the regex; it does not accept all possible int
values but will reject all invalid)
Upvotes: 3
Reputation: 29520
The @Assilyas answer is correct. I would like to propose an alternative based on the guava utility Ints::tryParse
:
Unlike
Integer.parseInt(String)
, this method returns null instead of throwing an exception if parsing fails
With it and the new method references, you could write:
Arrays.asList("1", "2", "3", "cheese", "5")
.stream()
.map(Ints::tryParse)
.map(Optional::ofNullable)
.forEach(v -> System.out.println(v.orElse(999)));
Upvotes: 1
Reputation: 206796
You could do this if you don't want the member variable ans
:
try {
return Optional.<Integer>of(Integer.parseInt(line));
} catch (NumberFormatException xep) {
return Optional.<Integer>empty();
}
Type inference is complicated and it has limitations, if you really want to know why this happens in this case you'd have to study the Java Language Specification.
Upvotes: 1
Reputation: 328598
A simple fix is to use a target type:
return Optional.<Integer> empty();
Also I note that you use Integer.parseInt
which returns an int
, so you could also use an OptionalInt
which will solve your problem and save a boxing operation:
try {
return OptionalInt.of(Integer.parseInt(line));
} catch (NumberFormatException xep) {
return OptionalInt.empty();
}
Upvotes: 7