| Summary: | Optional.orElse() doesn't accept null argument when parameter type is @NonNull | ||||||
|---|---|---|---|---|---|---|---|
| Product: | [Eclipse Project] JDT | Reporter: | Colin Bartolome <cbartolo> | ||||
| Component: | Core | Assignee: | Stephan Herrmann <stephan.herrmann> | ||||
| Status: | VERIFIED WORKSFORME | QA Contact: | |||||
| Severity: | normal | ||||||
| Priority: | P3 | CC: | manoj.palat, slaurent, stephan.herrmann | ||||
| Version: | 4.5.2 | ||||||
| Target Milestone: | 4.6 M7 | ||||||
| Hardware: | PC | ||||||
| OS: | Linux | ||||||
| Whiteboard: | |||||||
| Attachments: |
|
||||||
(In reply to Colin Bartolome from comment #0) > Created attachment 260551 [details] > OrElseNull.java shows the problem > > The documentation for Optional.orElse() explicitly allows code to pass a > null value as the argument, Yes, the documentation says so. The issue is: compilers don't read documentation :) but we do have a solution, hang on ... > In some cases, though, type inference produces an > Optional of type <@NonNull T> and Eclipse forbids us from passing null in > this case. Correct, if you have an Optional<@NonNull Bar> then orElse automatically has this signature: @NonNull Bar orElse(@NonNull Bar other) The tool for resolving this is help.eclipse.org/topic/org.eclipse.jdt.doc.user/tasks/task-using_external_null_annotations.htm More specifically: configure your project as explained in the help, then navigate to the parameter of Optional.orElse and invoke the "Annotate" command (Ctrl+1) and choose "Annotate as '@Nullable T'". The same for the return type of the same method. Henceforth the compiler will know about this contract and no longer complains about wrong usage of orElse(). Similar information (with pictures) can also be found in http://www.eclipse.org/eclipse/news/4.5/jdt.php#Annotations I should add that you need Eclipse 4.5 or newer for the solution (the last link in comment 1 already hinted at this). I got my Eclipse version wrong in the initial report; this happens in 4.5.2, not 4.4.2. Thanks for the info about the external annotations! Verified for Eclipse Neon 4.6 M7 Build id: I20160425-1300 (In reply to Stephan Herrmann from comment #1) > More specifically: configure your project as explained in the help, then > navigate to the parameter of Optional.orElse and invoke the "Annotate" > command (Ctrl+1) and choose "Annotate as '@Nullable T'". The same for the > return type of the same method. Actually this would break the contract of orElse, which returns a T. So, if you have a Optional<@NonNull String>, then orElse is supposed to return a @NonNull String... Ideally the signature of orElse should be (with T coming from the class declaration Optional<T> ): public <U super T> U orElse(final U other) but this is not allowed by the java language :-( I currently use a workaround with this helper method : public static <T> T optionalGetOrElse(final Optional<@NonNull T> opt, final T orElse) { if (opt.isPresent()) { return opt.get(); } return orElse; } It works with such usage : final @Nullable String nullableEntry = null; final String nullableString = NullCheckUtils.optionalGetOrElse(opt, nullableEntry); // on next line, eclipse sees a "potential null pointer access" nullableString.toString(); But weirdly eclipse 4.6 final fails to detect this : final String nullableString = NullCheckUtils.optionalGetOrElse(opt, null); // on next line, eclipse should have detected an error, but eclipse 4.6 does not nullableString.toString(); I also tried this signature for my optionalGetOrElse method: public static <U, T extends U> U optionalGetOrElse(final Optional<T> opt, final U orElse) but then eclipse does not detect any error in both usages :-( (In reply to Sylvain Laurent from comment #5) > (In reply to Stephan Herrmann from comment #1) > > More specifically: configure your project as explained in the help, then > > navigate to the parameter of Optional.orElse and invoke the "Annotate" > > command (Ctrl+1) and choose "Annotate as '@Nullable T'". The same for the > > return type of the same method. > > Actually this would break the contract of orElse, which returns a T. So, if > you have a Optional<@NonNull String>, then orElse is supposed to return a > @NonNull String... That's not how I read the javadoc. 'other' is explicitly allowed to be null hence the parameter and return type of 'orElse' should read '@Nullable T'. My advice from comment 1 only formalizes an existing contract. > But weirdly eclipse 4.6 final fails to detect this : > final String nullableString = NullCheckUtils.optionalGetOrElse(opt, > null); > // on next line, eclipse should have detected an error, but eclipse > 4.6 does not > nullableString.toString(); I see, so a '@Nullable T' is taken into account during type inference, but 'null' seems not to be. If so, this could be improved. May I ask you to file a new enhancement request with a self-contained example for this? TIA. done: Bug 498530 |
Created attachment 260551 [details] OrElseNull.java shows the problem The documentation for Optional.orElse() explicitly allows code to pass a null value as the argument, which will cause null to be returned if the Optional is empty. In some cases, though, type inference produces an Optional of type <@NonNull T> and Eclipse forbids us from passing null in this case. This tends to come up when calling Stream.findFirst() on a stream that started off being of type <@NonNull T>. The attached code shows an example similar to production code that's going to need a workaround.