Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Language IDEs » Java Development Tools (JDT) » @NonNullByDefault Questions
@NonNullByDefault Questions [message #1518297] Sat, 20 December 2014 01:52 Go to next message
Chris Hubick is currently offline Chris HubickFriend
Messages: 12
Registered: July 2009
Junior Member
When the @NonNullByDefault Javadoc says:

Quote:
Note: Since org.eclipse.jdt.annotation 2.0.0, this annotation also applies to field and local variable declarations


Does it really mean to say something more like:

Quote:
Note: Since org.eclipse.jdt.annotation 2.0.0, this annotation may also be used to annotate field and local variable declarations


?

I'm asking because I was initially reading that sentence as saying any local variables would actually be considered non-null by default, so I'm then confused by even a simple test case...

import org.eclipse.jdt.annotation.*;

@NonNullByDefault
public class Test {

  public static final String test() {
    final String s = "test".intern();
    return s; // Why do I get a warning here "Null type safety: The expression of type 'String' needs unchecked conversion to conform to '@NonNull String'"?
  }

}


Now, if I remove the intern() call, the warning on the return disappears, which leads me to believe the nullability of s is determined by some inferencing, which wouldn't be needed if it were actually affected by the @NonNullByDefault?

Thanks!

[Updated on: Sat, 20 December 2014 05:31]

Report message to a moderator

Re: @NonNullByDefault Questions [message #1518553 is a reply to message #1518297] Sat, 20 December 2014 05:20 Go to previous messageGo to next message
Chris Hubick is currently offline Chris HubickFriend
Messages: 12
Registered: July 2009
Junior Member
My other question relates to the Javadoc for org.eclipse.jdt.annotation.DefaultLocation, which first says:

Quote:
Wildcards and the use of type variables are always excluded from @NonNullByDefault


But then TYPE_BOUND goes on to provide an example:

@NonNullByDefault(TYPE_BOUND)
 interface X {
     <T extends Number> void process(T t, List<? super Number> l);
 }


Which says:

Quote:
Here both occurrences of Number will be interpreted as @NonNull Number.


So, the first occurrence is straightforward enough, but the second is a little confusing, given the previous statement about wildcards.

Is this saying that if it were simply List<?>, instead of List<? super Number>, then the contents could be null? Should the original statement be qualified to something like "unbounded wildcards"?

Thanks!
Re: @NonNullByDefault Questions [message #1519498 is a reply to message #1518553] Sat, 20 December 2014 18:15 Go to previous messageGo to next message
Stephan Herrmann is currently offline Stephan HerrmannFriend
Messages: 1853
Registered: July 2009
Senior Member
First regarding @NNBD vs. local variables:

I admit the phrase "applies to" could be improved.

There's a difference between
- where the annotation can be written, this defines the scope of the annotation
- which types within that scope are affected by the annotation.

The latter is controlled by the NNBD#value() attribute. Looking at the enum DefaultLocation you will see there isn't even an element LOCAL_VARIABLE. This implies that @NNBD will never directly affect a local variable. So what is the connection? See this example:

@NonNullByDefault Map<Object, Set<String>> myMap;


This is a short hand for writing:
Map<@NonNull Object, @NonNull Set<@NonNull String>> myMap;


IOW: We defined a @NNBD scoped to this particular variable (field or local), but the effect is constraint to the select elements of type DefaultLocation, in the example it's the TYPE_ARGUMENT element at work.

Is this clearer now?
Stephan

Re: @NonNullByDefault Questions [message #1519553 is a reply to message #1519498] Sat, 20 December 2014 18:55 Go to previous messageGo to next message
Chris Hubick is currently offline Chris HubickFriend
Messages: 12
Registered: July 2009
Junior Member
Stephan Herrmann wrote on Sat, 20 December 2014 13:15
IOW: We defined a @NNBD scoped to this particular variable (field or local), but the effect is constraint to the select elements of type DefaultLocation


That clarifies things, thanks.

I was aware of DefaultLocation for toggling the effects of @NNBD, but was viewing it more as something providing configuration options for @NNBD behaviour, in places where that behaviour could be customized, not so much as a constraint on it's behavior in other situations.

This makes me a little sad there isn't a DefaultLocation.LOCAL_VARIABLE configured for @NNBD by default, as I'd rather not rely on the inferencing, as the resulting warnings often show up in what feels like the wrong places (e.g. in my example, where I intended for 's' to be non-null), and that means having to annotate all local variables individually to avoid it. I guess it's good that you can use @NNBD as a "shorthand" while doing so, but that still doesn't really appear to save a whole lot. Maybe my desire to annotate local variables will lessen in the future when there's less un-annotated legacy code to throw off the inferencing. Confused

[Updated on: Sat, 20 December 2014 19:10]

Report message to a moderator

Re: @NonNullByDefault Questions [message #1519665 is a reply to message #1519553] Sat, 20 December 2014 20:30 Go to previous messageGo to next message
Stephan Herrmann is currently offline Stephan HerrmannFriend
Messages: 1853
Registered: July 2009
Senior Member
Regarding wildcards: I see your point, but saying "unbounded wildcards" would actually be wrong in this particular situation.

My logic goes: this example:
List<? super Number>

is affected by @NNBD(TYPE_BOUND) to become:
List<? super @NonNull Number>

which literally means, the "?" is still unannotated but its bound is annotated. Does this mean the annotation affects the wildcard?
No:
In fact null is still legal even in the presence of that super bound, since @Nullable Number is a supertype of @NonNull Number (which means the example of "super @NonNull X" is not a very usefull thing to express in the first place Smile ).

In this particular piece of javadoc my intention was to make clear statement about the special role of type variable use and wildcards. After my explanation: do you have a suggestion how that sentence could avoid the confusion you ran into without wrong implications (like wrongly expanding the example to "@NonNull ? super Number")?

In fact, I don't understand why you correctly understood the "T extends Number" case, but got confused on "? super Number" Razz

best,
Stephan
Re: @NonNullByDefault Questions [message #1523417 is a reply to message #1519665] Mon, 22 December 2014 22:54 Go to previous messageGo to next message
Chris Hubick is currently offline Chris HubickFriend
Messages: 12
Registered: July 2009
Junior Member
Heh, I'm still waiting for my brain to merge the git pull request on the patch I sent it for discerning between a TYPE_ARGUMENT and a TYPE_PARAMETER/variable Wink

I've definitely been viewing this in an overly left-hand-centric way, thinking of 'T' and '?' as specifying the type (and where @NNBD would apply), and not lending enough importance to the bound on the right. Your example helps clarify what's going on, and that wildcards are, in fact, not affected by @NNBD. Thanks!

I had been wracking my brain over how @NNBD would impact code like
@NonNullByDefault public <T,R> void exampleMethod(final Function<? super T,? extends R> funcArg) { .. }
, trying to figure out how, if type bounds are impacted by @NNBD, to keep funcArg unconstrained. Moving from concentrating on the '?' (which I now understand won't be affected), over to concentrating on the 'T' and 'R' instead, I think, with those being variables, that @NNBD shouldn't impact funcArg at all then?

I've also been tripped up a bit by some of the early bugs in this stuff, so I'm hoping with those out of the way it'll be easier to get a solid grip on it. Thanks for all your work and patience w questions Smile
Re: @NonNullByDefault Questions [message #1530471 is a reply to message #1523417] Fri, 26 December 2014 23:01 Go to previous messageGo to next message
Chris Hubick is currently offline Chris HubickFriend
Messages: 12
Registered: July 2009
Junior Member
import java.io.*;
import java.util.*;
import java.util.function.*;
import org.eclipse.jdt.annotation.*;

@NonNullByDefault
public class Test {

  public static final <T,R> Optional<R> applyOptional(final T input, final Function<? super T,? extends R> function) {
    return Optional.ofNullable(function.apply(input));
  }

  public static final <T,R> @NonNull R applyRequired(final T input, final Function<? super T,? extends R> function) { // Warning on '@NonNull R': "The nullness annotation is redundant with a default that applies to this location"
    return Objects.requireNonNull(function.apply(input));
  }

  public static final @Nullable Integer readIntegerFile(final String fileName) {
    final File file = new File(fileName);
    if (!file.canRead()) return null;
    try (Scanner scanner = new Scanner(new FileReader(fileName))) {
      return scanner.useDelimiter("\\A").hasNext() ? Integer.valueOf(scanner.next()) : null;
    } catch (IOException ioe) {
      throw new UncheckedIOException(ioe);
    }
  }

  public static final void main(final String[] args) {
    if (applyRequired("sun.arch.data.model", Integer::getInteger) >= 64) {
      System.err.println(applyOptional("does_not_matter.txt", Test::readIntegerFile).map(Object::toString).orElse("null"));
    } else {
      System.err.println(applyRequired("we_need_this.txt", Test::readIntegerFile).toString());
    }
    return;
  }

}


I disagree with this warning. Is this a bug ( https://bugs.eclipse.org/bugs/show_bug.cgi?id=438012 )? Why is that @NonNull redundant, given the @NNBD doesn't include DefaultLocation.TYPE_PARAMETER?

Thanks!

[Updated on: Sat, 27 December 2014 19:40]

Report message to a moderator

Re: @NonNullByDefault Questions [message #1530938 is a reply to message #1530471] Sat, 27 December 2014 05:21 Go to previous messageGo to next message
Chris Hubick is currently offline Chris HubickFriend
Messages: 12
Registered: July 2009
Junior Member
I'm curious how null annotations interact with java.util.Optional...

Optional, by it's very nature, is something which is @Nullable (may or may not have a value). One might initially be inclined to think that would indicate it's type parameter normally be a @Nullable type.

But, when you look a bit closer, the Optional API is all about not exposing that type unless it's non-null. The primary get() method will only return the contents when non-null, otherwise throwing a NoSuchElementException. The Optional.ifPresent(Consumer<T>) would only ever see a non-null argument, etc, etc. This makes me think the Optional class's type parameter should normally be a @NonNull type, where the primary constructor method Optional.of(T) would then automatically be restricted to non-null arguments, as per it's current API/implementation restrictions, and methods like Optional.ofNullable(T) would then just have their parameter overriden to be @Nullable. Should Optional generally use a @NonNull type?

That leads me to Optional.orElse(T)...

I've been making increasing use of Optional as a return type, but it seems less suited for use as a parameter type, and there are a lot of (legacy?) methods which simply accept null arguments. As a result, I often find myself needing to call a '@Nullable T' method when I have an 'Optional<T>' parameter, and as a result I've ended up with a substantial number of inline Optional.orElse(null) calls in those places.

The thing is, null analysis obviously doesn't like Optional<@NonNull T>.orElse(null), given the orElse() method expects a parameter of the same @NonNull T type.

So, to rid myself of these warnings, I'm tempted to head down a path like: Optional<@NonNull T>.<@Nullable T> map(Function.identity()).orElse(null). That is, use the map() method to convert it from a '@NonNull T' to a '@Nullable T' before invoking orElse(null) (you can also use Optional<@Nullable T>.<@NonNull R> map(Objects::requireNonNull) to convert the other direction from @Nullable to @NonNull). The ability to do things like this is why I don't think Optional should actually be restricted to only @NonNull types.

But those solutions leave me in a situation where I'm taking code I know already operates perfectly well before null annotations, and adding in extra operations that could have a real and measurable impact on system performance, just to satisfy compiler warnings. I hesitate.

I don't really know where to RTFM on this kind of thing. Advice?

Thanks!

[Updated on: Sun, 28 December 2014 02:00]

Report message to a moderator

Re: @NonNullByDefault Questions [message #1540563 is a reply to message #1530471] Thu, 01 January 2015 15:31 Go to previous messageGo to next message
Stephan Herrmann is currently offline Stephan HerrmannFriend
Messages: 1853
Registered: July 2009
Senior Member
Chris Hubick wrote on Sat, 27 December 2014 00:01

I disagree with this warning. Is this a bug ( https://bugs.eclipse.org/bugs/show_bug.cgi?id=438012 )? Why is that @NonNull redundant, given the @NNBD doesn't include DefaultLocation.TYPE_PARAMETER?


I agree with your disagreeing Smile, but I don't see a direct connection to the bug you reference. Could you please file a new bug? I believe the check for redundant annotations fails to apply the rule that wildcards and the use of type variables are always excluded from @NNBD.

Thanks,
Stephan
Re: @NonNullByDefault Questions [message #1540642 is a reply to message #1540563] Thu, 01 January 2015 16:36 Go to previous messageGo to next message
Stephan Herrmann is currently offline Stephan HerrmannFriend
Messages: 1853
Registered: July 2009
Senior Member
Re Optional:

Disclaimer: I've never actively used Optional, so I might be missing some clever usage thereof.

My understanding is that Optional is intended as a replacement for null altogether. This would let me think: yes, all signatures of Optional should operate on @NonNull something or throw exceptions. So if you have (legacy) parts for the program where null is a legal value, and new parts, where Optional is used consistently instead of null, you'll need some bridge between them two parts (assuming you know where the boarder is drawn).

The naive bridge might look like this:
interface UniverseWithNull {
     void method1(@Nullable String s);
}
@NonNullByDefault
class UnviserveWithoutNull {
    void methodX(Optional<String> s, UniverseWithNull delegate) {
        delegate(s.isPresent() ? s.get() : null);
    }
}


Not particularly pretty, so how can we simplify? orElse() is a good candidate, indeed. We only hit the limitation that different occurrences of 'T' in the API of Optional cannot be distinguished, although some explicitly include null while most exclude null. To make the combination of Optional and null annotations useful, we'll need null annotations in the definition of Optional, s.t. like:
class Optional<T extends @NonNull Object> {
  @Nullable T value;
  private Optional(T value) { this.value = value; }
  public static <@NonNull T> Optional<T> of(T value) {
    return new Optional<T>(value);
  }
  public T get() {
    @Nullable T t = this.value;
    if (t != null) return t; 
    else throw new NoSuchElementException("No value present");
  }
  public @Nullable T orElse(@Nullable T other) {
    return (this.value != null) ? this.value : other;
  }
}


(See https://bugs.eclipse.org/456487 for issues found on my way ...)

One could also try to leave the declaration of <T> unconstrained and only add @NonNull to all regular usage of T (except for orElse etc.).

Anyway, this only takes us back to https://bugs.eclipse.org/331651, so let's push that one with high priority now!

best,
Stephan

Re: @NonNullByDefault Questions [message #1540707 is a reply to message #1519553] Thu, 01 January 2015 17:26 Go to previous messageGo to next message
Stephan Herrmann is currently offline Stephan HerrmannFriend
Messages: 1853
Registered: July 2009
Senior Member
Chris Hubick wrote on Sat, 20 December 2014 19:55

This makes me a little sad there isn't a DefaultLocation.LOCAL_VARIABLE ...


If you feel a strong need for that feel free to file a new RFE. We wouldn't include LOCAL_VARIABLE by default, but if your code benefits from its use, you'd be free to fine-tune NNBD accordingly.

So far I simply didn't see code where NNBD for locals would get better results than flow analysis. So an RFE would need a good motivating example to begin with Smile

best,
Stephan
Re: @NonNullByDefault Questions [message #1544700 is a reply to message #1540563] Sat, 03 January 2015 23:41 Go to previous messageGo to next message
Chris Hubick is currently offline Chris HubickFriend
Messages: 12
Registered: July 2009
Junior Member
Stephan Herrmann wrote on Thu, 01 January 2015 10:31
Chris Hubick wrote on Sat, 27 December 2014 00:01

I disagree with this warning. Is this a bug ( https://bugs.eclipse.org/bugs/show_bug.cgi?id=438012 )? Why is that @NonNull redundant, given the @NNBD doesn't include DefaultLocation.TYPE_PARAMETER?


I agree with your disagreeing Smile, but I don't see a direct connection to the bug you reference. Could you please file a new bug? I believe the check for redundant annotations fails to apply the rule that wildcards and the use of type variables are always excluded from @NNBD.


Done: https://bugs.eclipse.org/bugs/show_bug.cgi?id=456584 Smile
Re: @NonNullByDefault Questions [message #1544707 is a reply to message #1540642] Sat, 03 January 2015 23:49 Go to previous messageGo to next message
Chris Hubick is currently offline Chris HubickFriend
Messages: 12
Registered: July 2009
Junior Member
Stephan Herrmann wrote on Thu, 01 January 2015 11:36
To make the combination of Optional and null annotations useful, we'll need null annotations in the definition of Optional


As part of my response, I attempted to annotate Optional myself as well, but quickly hit this:

import java.util.*;
import java.util.function.*;
import org.eclipse.jdt.annotation.*;

public class Test {

  /**
   * A null-annotated version of {@link Objects#requireNonNull(Object)}.
   */
  public static final <T> @NonNull T requireNonNull(final @Nullable T obj) {
    if (obj == null) throw new NullPointerException();
    return obj;
  }

  /**
   * A null-annotated version of {@link Optional#map(Function)}.
   */
  public static final <T,U> @NonNull Optional<U> map(final @NonNull Optional<T> optional, final Function<@NonNull ? super T,? extends U> mapper) {
    if (!optional.isPresent()) return requireNonNull(Optional.empty());
    final T source = optional.get();
    final U result = mapper.apply(source);
    return requireNonNull(Optional.<U> ofNullable(result));
  }

  /**
   * A method with a {@link NonNull} {@link DefaultLocation#PARAMETER} and {@link DefaultLocation#RETURN_TYPE}.
   */
  public static final @NonNull Integer testMethod(final @NonNull String s) {
    final Integer r = Integer.valueOf(s);
    if (r == null) throw new NullPointerException();
    return r;
  }

  public static void main(final String[] args) {
    final @NonNull Optional<@Nullable String> optNullableString = requireNonNull(Optional.ofNullable("-5"));

    final Function<@NonNull String,@NonNull Integer> testMethodRef = Test::testMethod;
    map(optNullableString, testMethodRef);

    map(optNullableString, Test::testMethod); // Error: Null type mismatch at parameter 1: required '@NonNull String' but provided '@Nullable String' via method descriptor Function<String,Integer>.apply(String)
  }

}


Is that a bug, as it doesn't appear as though the lambda expression type inferencing from the method reference accounts for null annotations?

Thanks!
Re: @NonNullByDefault Questions [message #1544775 is a reply to message #1544707] Sun, 04 January 2015 00:40 Go to previous messageGo to next message
Stephan Herrmann is currently offline Stephan HerrmannFriend
Messages: 1853
Registered: July 2009
Senior Member
Chris Hubick wrote on Sun, 04 January 2015 00:49
Is that a bug, as it doesn't appear as though the lambda expression type inferencing from the method reference accounts for null annotations?


That error looks weird, let me see, where could the @Nullable come from?

Ah: look:
@NonNull Optional<@Nullable String> optNullableString

From passing this variables as first arg into map(), inference undeniably has to instantiate map's <T> as '@Nullable String'.

That probably makes the second parameter of map:
Function<@NonNull ? super @Nullable String, something>


outch, do we really accept that? This type looks like nonsense: Supertype of a @Nullable type and being @NonNull itself is an unsatisfiable type.

Apparently, the @Nullable part wins and makes the SAM for the method reference have a @Nullable method parameter. Since testMethod requires a @NonNull argument it is not a valid implementation of the SAM. In this vein the error makes sense.

If you change the type of 'optNullableString' to
@NonNull Optional<@NonNull String> optNullableString

the program compiles fine (in HEAD).

Still accepting the strange wildcard looks wrong, perhaps(?) related to https://bugs.eclipse.org/448709

thanks,
Stephan

[Updated on: Sun, 04 January 2015 00:45]

Report message to a moderator

Re: @NonNullByDefault Questions [message #1544902 is a reply to message #1544775] Sun, 04 January 2015 02:25 Go to previous messageGo to next message
Chris Hubick is currently offline Chris HubickFriend
Messages: 12
Registered: July 2009
Junior Member
Stephan Herrmann wrote on Sat, 03 January 2015 19:40
That probably makes the second parameter of map:
Function<@NonNull ? super @Nullable String, something>


outch, do we really accept that? This type looks like nonsense: Supertype of a @Nullable type and being @NonNull itself is an unsatisfiable type.


Yeah, I think what I should have written was simply this:

public static final <T,U> @NonNull Optional<U> map(final @NonNull Optional<T> optional, final Function<? super @NonNull T,? extends U> mapper)


What I was aiming for was to have the free-type Optional<T>, but want the 'mapper' function to only have to support accepting a @NonNull parameter, since Optional.map() doesn't actually invoke the mapper function unless it's a non-null value (same for filter() with it's Predicate, etc).

[Updated on: Sun, 04 January 2015 03:09]

Report message to a moderator

Re: @NonNullByDefault Questions [message #1545005 is a reply to message #1540642] Sun, 04 January 2015 03:54 Go to previous messageGo to next message
Chris Hubick is currently offline Chris HubickFriend
Messages: 12
Registered: July 2009
Junior Member
Stephan Herrmann wrote on Thu, 01 January 2015 11:36
To make the combination of Optional and null annotations useful, we'll need null annotations in the definition of Optional, s.t. like:
class Optional<T extends @NonNull Object> {
  public @Nullable T orElse(@Nullable T other) {
    return (this.value != null) ? this.value : other;
  }
}



I don't think Optional.orElse() should/could be annotated to always accept/return @Nullable. As convenient as orElse(null) is for dealing with 'legacy' code, you'd create way more problems than you'd solve, since I expect the vast majority of clients in new codebases will likely demand the method return the same nullness of <T> as the host Optional itself is.

Stephan Herrmann wrote on Thu, 01 January 2015 11:36
One could also try to leave the declaration of <T> unconstrained and only add @NonNull to all regular usage of T (except for orElse etc.).


I think that's the more flexible approach if you don't want to leave use-cases like orElse(null) dead in the water. Here's a first draft:

import java.util.*;
import java.util.function.*;
import org.eclipse.jdt.annotation.*;

public final class Optional<T> {
  private static final @NonNull Optional<?> EMPTY = new Optional<>();
  private final @Nullable T value;

  private Optional() {
    this.value = null;
  }

  public static <T> @NonNull Optional<T> empty() {
    @SuppressWarnings("unchecked")
    @NonNull Optional<T> t = (Optional<T>)EMPTY;
    return t;
  }

  private Optional(@NonNull T value) {
    this.value = Objects.requireNonNull(value);
  }

  public static <T> @NonNull Optional<@NonNull T> of(@NonNull T value) {
    return new Optional<@NonNull T>(value);
  }

  public static <T> @NonNull Optional<T> ofNullable(@Nullable T value) {
    return value == null ? empty() : of(value);
  }

  public @NonNull T get() {
    final @Nullable T value = this.value;
    if (value == null) throw new NoSuchElementException("No value present");
    return value;
  }

  public boolean isPresent() {
    return value != null;
  }

  public void ifPresent(@NonNull Consumer<? super @NonNull T> consumer) {
    final @Nullable T value = this.value;
    if (value != null) consumer.accept(value);
  }

  public @NonNull Optional<T> filter(@NonNull Predicate<? super @NonNull T> predicate) {
    Objects.requireNonNull(predicate);
    final @Nullable T value = this.value;
    return (value != null) && predicate.test(value) ? this : empty();
  }

  public <U> @NonNull Optional<U> map(@NonNull Function<? super @NonNull T,? extends U> mapper) {
    Objects.requireNonNull(mapper);
    final @Nullable T value = this.value;
    return (value != null) ? Optional.<U> ofNullable(mapper.apply(value)) : empty();
  }

  public <U> @NonNull Optional<U> flatMap(@NonNull Function<? super @NonNull T,@NonNull Optional<U>> mapper) {
    Objects.requireNonNull(mapper);
    final @Nullable T value = this.value;
    return (value != null) ? Objects.requireNonNull(mapper.apply(value)) : empty();
  }

  public T orElse(T other) {
    final @Nullable T value = this.value;
    return value != null ? value : other;
  }

  public T orElseGet(@NonNull Supplier<? extends T> other) {
    final @Nullable T value = this.value;
    return value != null ? value : other.get();
  }

  public <X extends Throwable> @NonNull T orElseThrow(@NonNull Supplier<? extends @NonNull X> exceptionSupplier) throws X {
    final @Nullable T value = this.value;
    if (value != null) return value;
    throw exceptionSupplier.get();
  }

  @Override
  public boolean equals(@Nullable Object obj) {
    if (this == obj) return true;
    if (!(obj instanceof Optional)) return false;
    return Objects.equals(value, ((Optional<?>)obj).value);
  }

  @Override
  public int hashCode() {
    return Objects.hashCode(value);
  }

  @Override
  public @NonNull String toString() {
    final @Nullable T value = this.value;
    return value != null ? String.format("Optional[%s]", value) : "Optional.empty";
  }

}


The ofNullable allows clients to choose if they want a @NonNull or @Nullable result, and that let's you use map() to do "conversions" from Optional<@NonNull> to Optional<@Nullable> and then orElse(null) or orElse(anyNullable), etc on the result.

Stephan Herrmann wrote on Thu, 01 January 2015 11:36
Anyway, this only takes us back to https://bugs.eclipse.org/331651 so let's push that one with high priority now!


Yeah, I'm just trying to understand this stuff well enough to write code that will correctly anticipate that being fixed Wink

If you get the infrastructure completed and it looks like the textual encoding won't quickly follow, I'd settle for hardcoding annotations for java.util.Optional and java.util.Objects, as even just those two alone would be immensely useful Smile
Re: @NonNullByDefault Questions [message #1546136 is a reply to message #1545005] Sun, 04 January 2015 19:13 Go to previous messageGo to next message
Chris Hubick is currently offline Chris HubickFriend
Messages: 12
Registered: July 2009
Junior Member
Chris Hubick wrote on Sat, 03 January 2015 22:54
Stephan Herrmann wrote on Thu, 01 January 2015 11:36
One could also try to leave the declaration of <T> unconstrained and only add @NonNull to all regular usage of T (except for orElse etc.).


I think that's the more flexible approach if you don't want to leave use-cases like orElse(null) dead in the water.


Yeah, playing around with this a bit more, I'm starting to think the whole concept of "conversions" and Optional<@Nullable> just can't be made to work elegantly, and you just shouldn't do things like orElse(null).

The Optional API has of(), ofNullable(), and orElse() - and what it really needed was an orElseNullable() to go with those.

Anyhow, here's a take on the whole thing restricted to @NonNull...

import java.util.*;
import java.util.function.*;

import org.eclipse.jdt.annotation.*;

public final class Optional<T extends @NonNull Object> {
  private static final @NonNull Optional<@NonNull ?> EMPTY = new Optional<>();
  private final @Nullable T value;

  private Optional() {
    this.value = null;
  }

  public static <@NonNull T> @NonNull Optional<T> empty() {
    @SuppressWarnings("unchecked")
    @NonNull Optional<T> t = (Optional<T>)EMPTY;
    return t;
  }

  private Optional(T value) {
    this.value = Objects.requireNonNull(value);
  }

  public static <@NonNull T> @NonNull Optional<T> of(T value) {
    return new Optional<T>(value);
  }

  public static <@NonNull T> @NonNull Optional<T> ofNullable(@Nullable T value) {
    return value == null ? empty() : of(value);
  }

  public T get() {
    final @Nullable T value = this.value;
    if (value == null) throw new NoSuchElementException("No value present");
    return value;
  }

  public boolean isPresent() {
    return value != null;
  }

  public void ifPresent(@NonNull Consumer<? super T> consumer) {
    final @Nullable T value = this.value;
    if (value != null) consumer.accept(value);
  }

  public @NonNull Optional<T> filter(@NonNull Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    final @Nullable T value = this.value;
    return (value != null) && predicate.test(value) ? this : empty();
  }

  public <@NonNull U> @NonNull Optional<U> map(@NonNull Function<? super T,? extends U> mapper) {
    Objects.requireNonNull(mapper);
    final @Nullable T value = this.value;
    return (value != null) ? Optional.<U> ofNullable(mapper.apply(value)) : empty();
  }

  public <@NonNull U> @NonNull Optional<U> flatMap(@NonNull Function<? super T,@NonNull Optional<U>> mapper) {
    Objects.requireNonNull(mapper);
    final @Nullable T value = this.value;
    return (value != null) ? Objects.requireNonNull(mapper.apply(value)) : empty();
  }

  public @Nullable T orElse(@Nullable T other) {
    final @Nullable T value = this.value;
    return value != null ? value : other;
  }

  public @Nullable T orElseGet(@NonNull Supplier<? extends @Nullable T> other) {
    final @Nullable T value = this.value;
    return value != null ? value : other.get();
  }

  public <X extends @NonNull Throwable> T orElseThrow(@NonNull Supplier<? extends X> exceptionSupplier) throws X {
    final @Nullable T value = this.value;
    if (value != null) return value;
    throw exceptionSupplier.get();
  }

  @Override
  public boolean equals(@Nullable Object obj) {
    if (this == obj) return true;
    if (!(obj instanceof Optional)) return false;
    return Objects.equals(value, ((Optional<@NonNull ?>)obj).value);
  }

  @Override
  public int hashCode() {
    return Objects.hashCode(value);
  }

  @Override
  public @NonNull String toString() {
    final @Nullable T value = this.value;
    return value != null ? String.format("Optional[%s]", value) : "Optional.empty";
  }

}

[Updated on: Sun, 04 January 2015 22:31]

Report message to a moderator

Re: @NonNullByDefault Questions [message #1546373 is a reply to message #1545005] Sun, 04 January 2015 22:30 Go to previous message
Chris Hubick is currently offline Chris HubickFriend
Messages: 12
Registered: July 2009
Junior Member
Chris Hubick wrote on Sat, 03 January 2015 22:54
Stephan Herrmann wrote on Thu, 01 January 2015 11:36
To make the combination of Optional and null annotations useful, we'll need null annotations in the definition of Optional, s.t. like:
class Optional<T extends @NonNull Object> {
  public @Nullable T orElse(@Nullable T other) {
    return (this.value != null) ? this.value : other;
  }
}



I don't think Optional.orElse() should/could be annotated to always accept/return @Nullable. As convenient as orElse(null) is for dealing with 'legacy' code, you'd create way more problems than you'd solve, since I expect the vast majority of clients in new codebases will likely demand the method return the same nullness of <T> as the host Optional itself is.


And I just noticed that the Javadoc for orElse() explicitly specifies that the 'other' paramater "may be null". So, I guess that settles that then Confused
Previous Topic:Luna - Run Configurations: "Eclipse Application" not found
Next Topic:Eclipse menu hiding using plugin
Goto Forum:
  


Current Time: Fri Apr 26 22:18:35 GMT 2024

Powered by FUDForum. Page generated in 0.05717 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top