Java 8 Optional.ifPresent is my code wrong or is it eclipse?

1.4k views Asked by At

I am new to Java 8 and trying out Null type annotations and Optional.

For my example below, I have used String rather than my class and am calling toUpperCase just to call something, in my case I actually call a function passing in a parameter (so don't think I can use :: operator and/or maps).

In Eclipse I have the Java - Compiler - Errors/Warnings - Null Analysis Errors turned on.

My test code below:

public void test1(@Nullable String s) {
    // the 2nd s2 has a Potential null pointer access error. 
    // I was hoping ifPresent would imply NonNull
    Optional.ofNullable(s).ifPresent(s2 -> s2.toUpperCase());
}

@Nullable 
public String getSomeString() {
    return null;
}

public void test2() {
    String s = getSomeString();

    // This is fine, unlike the first example, I would have assumed that
    // it would know s was still nullable and behave the same way.
    Optional.ofNullable(s).ifPresent(s2 -> s2.toUpperCase());
}

It would seem that using Eclipse type null annotations and Optional.ifPresent doesn't go well together.

Am I wasting my time trying to get something like this to work? Should I just revert back to assigning the getter to a temp var then checking if null, and if not call my function?

2

There are 2 answers

4
Stephan Herrmann On BEST ANSWER

JDT's null analysis cannot know about the semantics of each and every method in JRE and other libraries. Therefore, no conclusions are drawn from seeing a call to ifPresent. This can be remedied by adding external annotations to Optional so that the analysis will see method ofNullable as

<T> Optional<@NonNull T> ofNullable(@Nullable T value)

External annotations are supported starting with Eclipse Mars, released June, 24, 2015. See Help: Using external null annotations.

The difference between the two variants in the question is due to how null analysis is integrated with Java 8 type inference: In variant (1) s has type @Nullable String. When this type is used during type inference, it is concluded that the argument to ifPresent is nullable, too. In variant (2) s has type String (although flow analysis can see that is may be null after the initialization from getSomeString). The unannotated type String is not strong enough to aid type inference to the same conclusion as variant (1) (although this could possibly be improved in a future version of JDT).

3
Harmlezz On

First of: @Nullable seams not to be part of the public Java 8 SDK. Have a look at the package you imported: com.sun.istack.internal.Nullable.

Second: I have run both of your methods: test1(null) and test2() and nothing out of the ordinary happened. Everything was fine (as expected). So what did you observe?

run test1(null) => no execution of lambda expression.

run test2() => no execution of lambda expression.

I changed your code for testing in the following way:

public void test1(@Nullable String s) {
    Optional.ofNullable(s).ifPresent(s2 -> System.out.println("executed"));
}

public void test2() {
    String s = getSomeString();
    Optional.ofNullable(s).ifPresent(s2 -> System.out.println("executed"));
}