Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type inference on lambdas

Tags:

java-8

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?

like image 304
Peter Hull Avatar asked Dec 19 '22 10:12

Peter Hull


1 Answers

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();
}
like image 50
assylias Avatar answered Feb 24 '23 19:02

assylias