Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generic userdoc confusion #500

Open
xenoterracide opened this issue Mar 23, 2024 · 7 comments
Open

generic userdoc confusion #500

xenoterracide opened this issue Mar 23, 2024 · 7 comments

Comments

@xenoterracide
Copy link

Looking at the first example

public interface List<E extends @Nullable Object> {
  boolean add(E element);
  E get(int index);

is E get(int index); automatically @Nullable here?

what if List were defined as

public interface List<E> {

and then

public class ImmutableList<@Nullable E> {

would E get( on ImmutableList automatically be nullable?

P.S. the docs say that getFirst is fictitious which is no longer true as of java 21.

@agentgt
Copy link

agentgt commented Mar 23, 2024

P.S. the docs say that getFirst is fictitious which is no longer true as of java 21.

Ha that is a good find!

is E get(int index); automatically @nullable here?

No. It is whatever the declaration is. Let us assume we are in code bases with NullMarked.

List<String> list = ...;
var e = list.get(0); // e is nonnull here.

and

List<@Nullable String> list = ...;
var e = list.get(0); // e is nullable here.

Again assuming a @NullMarked code base that the interface List is in:

public interface List<E> {

Means E is always nonnull. I guess you could think of it as List<E extends @NonNull Object>.
There are some tools that allow flexibility on this but essentially parameter definitions are nonnull unless marked as nullable.

public class ImmutableList<@Nullable E> {

I assume the above implements our nonnull List of List<E> and not the extends nullable of the actual java.util.List right ?

The tools currently will allow it but I think in JSpecify in spirit should be an error.

@xenoterracide
Copy link
Author

xenoterracide commented Mar 25, 2024

public interface List<E extends @Nullable Object>

so why do this?

Also, what happens if you have var list instead of List<String> list, these days I rarely need to write the latter.

I assume the above implements our nonnull List of List and not the extends nullable of the actual java.util.List right ?

yes, although, that is a good example of maybe why the examples shouldn't be List? it's not very common to be writing a List interface.

@cpovirk
Copy link
Collaborator

cpovirk commented Mar 28, 2024

public interface List<E extends @Nullable Object>

...lets users write both List<[@NonNull] String> and List<@Nullable String>. That way, users who don't want to put nulls into their lists don't have to deal with the nullable return from methods like get, but users who do want to put nulls in can still do so.

That's in contrast to...

public interface List<E [extends @NonNull Object]>

...which would allow only "one kind" of List. (That would force us to choose between "No lists contain nulls" (which is limiting) and "All lists may contain nulls" (which is inconvenient).)

I have a doc that has a list of types to demonstrate when various combination of nullnesses may be useful for generic types. Here's a snippet:

@NullMarked
interface Factory<T extends @NonNull Object> {
  T newInstance();
}

@NullMarked
interface BlockingQueue<E extends @NonNull Object> {
  @Nullable E peek();
}

@NullMarked
interface Supplier<T extends @Nullable Object> {
  T get();
}

@NullMarked
interface NavigableSet<E extends @Nullable Object> {
  @Nullable E pollFirst();
}

public class ImmutableList<@Nullable E> {

As noted, we define that as an inapplicable location for @Nullable. If you want to say that ImmutableList is a list that always permits null elements, then you'd write:

public class ImmutableList<E> implements List<@Nullable E>

Guava actually has one weird type like that, ArrayTable, and a couple less weird types like that, Equivalence and Converter. (Hmm, no, wait, we didn't actually annotate Converter to have a supertype of Function<@Nullable A, @Nullable B>, only Function<A, B>. But the other two examples stand.)

Also, what happens if you have var list instead of List<String> list, these days I rarely need to write the latter.

The type would be inferred from the right-hand side of the assignment (e.g., new ArrayList<String>() or new ArrayList<@Nullable String>()). If you use the diamond operator on the right-hand side, then, well, we have thus far left type inference up to tools :( I'd kind of expect them to infer List<@Nullable Object>, similar to how javac (I think) infers List<Object> in the same situation, but I haven't thought about this much.

@cpovirk
Copy link
Collaborator

cpovirk commented Mar 28, 2024

(And yes, the real thing we need to do here is to improve our docs.)

@agentgt
Copy link

agentgt commented Mar 28, 2024

Sort of off topic but I guess I missed that @NonNull was added (hence my hesitation to actually use the annotation for example).

Was it always available? I swear its not in JSpecify 0.3.

EDIT I guess I missed it: https://github.com/jspecify/jspecify/releases

@agentgt
Copy link

agentgt commented Mar 28, 2024

@cpovirk

Now that I'm looking at the Javadoc of NonNull a note should probably be added either below or addition to:

Where it is not applicable
@NonNull is inapplicable in all the same locations as Nullable.

That you should not combine the @Nullable and @NonNull annotations on the exact same spot.

I'm not even sure what checker does in that case but I assume its an error. I'll check later (pun intended).
@NonNull is inapplicable in all the same locations as Nullable.

EDIT never mind. I'm slow today I see it now on Nullable javadoc:

If both @Nullable and @NonNull appear on the same type usage, neither one is recognized.

@cpovirk
Copy link
Collaborator

cpovirk commented Mar 28, 2024

Well, I was about to boldly claim that "@Nullable @NonNull means nothing" is not consequential enough to be among the first 400 words that I would want people to read about @NonNull. (We do expect to have to provide lots of docs beyond the Javadoc, so there's a balancing act for what to include there.) I guess that claim doesn't look so good now that you've determined that we already say it :) (But maybe it's reasonable to include when we get 1k words to talk about @Nullable!)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants