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
Nullness: Annotating wildcards and their bounds #31
Comments
Clearly the parallel is strong for
I am pretty convinced this is the right answer.
I said in our meeting that this means we "have to" do something, but now I'm really scratching my head as to how/why this problem will come up in real life, and would kinda like to wait until it does.
Well, I learned something today. I'm not sure it used to be like this. But I guess it makes sense that there's no harm in referring to it by a more general type while in reality it will always be more specific.
Presumably we should intersect them.
As far as I can see, this all makes sense. Ignoring legacy nullness for the time being:
Now (note these are slightly out of order):
The
I've convinced myself to agree.
"I need you to give me something that can't do X" is a negative contract, and as such, I don't think we need to worry about it. I am capable of ensuring I don't add nulls to the list whether you pass me one that rejects nulls or not. I would be very happy if we do NOT need to support constraining both bounds at once. There hasn't been a way to do that in base Java types (mostly) and it's been just fine. |
...
If we are in a Therefore, we would need to write
or think about different rules. |
Oh. That's not what I assumed it would mean. My perspective is that an upper bound is not present and thus there is nothing to default. The lower bound is present and it is what would default to @NotNull, but in the example it is given explicitly so the default never applies. Applying (aside: I have claimed that Java non-invariant types have either a lower or upper bound but not both, however, I do realize that a |
With the slight caveat in the case of |
After reading the comments above more carefully I propose that we treat the implicit upper bounds of
And thus not allow annotations on unbounded |
(True. This will happen, but my contention is that it's irregular enough that it's not worth contorting our design for. That's a very subtle judgment call, though.) |
Resolution from the meeting:
|
If that's really type parameter, not type argument, I wonder how - in the context of nonnull default - a type with generic nullness should be declared? If all type parameters are implicitly annotated clients can no longer choose during instantiation of the generic type. |
If the upper bound is class C<T extends @Nullable Object> {
T get() {...}
} The result of |
OK, this brings us to the question, what is expected to be more common / should be encouraged:
For me, the latter seems more natural, and so the former should get the burden of additional verbosity, not the latter. I don't have the empirical data to back this, though. |
I think the relevant discussion for your last point is here: #12 (comment) |
I would like to add some possible clarifications to the current proposal about wildcards and nullability. (Further, I assume that First of all, I'm totally in favor of the perception of wildcards as existential types. Meaning that we don't have any information besides their bounds. When considering nullability specifiers, I would suggest that they are only applicable to the bounds not to their occurrences of the existential type themselves. I.e., for any nullability annotations combination we should not treat any type as Nullability from type parameter's bounds, in this case, might be just intersected with nullability of existential type's bounds. So, the relevant piece from the proposal may be reworded as follows:
When typechecking wildcarded types, tools should use lower or upper bound of the relevant existential type depending on position:
|
Thanks, I like the idea of making
(which is in the context of a |
Yes, you're right. I've fixed the description.
Looks like you're right. Maybe it's worth discussing it at a meeting. |
I am trying to parse this thread. Let me ask this simple question though:
I think we are maybe agreeing that the default should be applied to every bound, so that it's as if you wrote:
... even though there is a slight oddness here: this makes the element type's nullness fixed for the first three (non-nullable) but happens to make it unconstrained for the last one. I believe that is actually a fine and natural consequence, but it bears pointing out. Aside from the overarching question of whether we should switch to Stephan's "orthogonal nullness and types" model -- apart from that, is what I've just said considered controversial at this point? (I am deliberately leaving bare |
I only disagree regarding the case of |
(Last comment is about #83.) I think Denis's comment #31 (comment) is an accurate depiction of how we are thinking about this now. |
What we seem to agree on
Foo<? extends @Nullabe Bar>
The current rule for type parameter bounds
In the context of
@DefaultNullable
/@DefaultNotNull
if a type parameter has no explicitly annotated bound, its bound is considered to be annotated according to the specified default.Questions
Foo<@NotNull ?>
,Foo<@NotNull ? extends @Nullable Bar>
,Foo<@NotNull ? super @Nullable Bar>
@DefaultNullable
/@DefaultNotNull
apply to the wildcard itself? (If we give that any meaning while answering the previous question)Foo<? extends Bar>
become effectivelyFoo<? extends @NotNull Bar>
when in the scope of@DefaultNotNull
?Unbounded wildcard (
?
)It seems that we might need to allow annotating an unbounded wildcard
Foo<@NotNull ?>
because its bound is not always denotable, e.g. for the case of F-bounded recursive types, e.g.:It looks like
C<?>
andC<? extends Object>
may not mean the same thing here, but in fact JLS §4.5.1 says this:And the following code compiles correctly:
The bounds from the declaration site are implicitly applied to the wildcard even if it declares only
Object
as its explicit bound, but then it's hard to tell how an annotation on theObject
should interact with those implicitly applied bounds. So, for now, we have it as an open question.Bounded wildcards
A wildcard type in Java can specify either an upper bound (
? extends Foo
) or a lower bound (? super Foo
). The bounds are normal type usages, so they can be annotated explicitly as@NotNull
or@Nullable
.By analogy with type parameter bounds, it would make sense to apply defaults to unannotated wildcard bounds. E.g.
Foo<? extends Bar>
becomes effectivelyFoo<? extends @NotNull Bar>
when in the scope of@DefaultNotNull
."Write-only list of not-null Foo"
The intuition for
List<? super Foo>
is roughly "a write-only list of Foo". One may want to say something like "write-only list of not-null Foo", or "a list where you can add only not-null Foo's".Observation:
List<? super @NotNull Foo>
does not capture this intent because it can be assignedList<@Nullable Foo>
(@Nullable Foo
is a supertype of@NotNull Foo
).To express this intent, one could use the fact that each wildcard implicitly has two bounds, so that
? super Foo
means actually "lower bound Foo, upper bound Object(and
? extends Foois "lower bounds Bottom, upper bound Foo", where Bottom is the subtype of all types, i.e. the empty type, like Kotlin's
Nothing`).So, the "write-only list where you can only add not-null Foo's" would be a list of "lower bound
@NotNull Foo
upper bound@NotNull Object
). We could adopt a convention that this can be expressed asList<@NotNull ? super @NotNull Foo>
where the annotation of the?
applies to the bound that is not explicit.Some more examples of this convention:
List<@NotNull Foo>
List<@Nullable Foo>
List<@NotNull ? super @NotNull Foo>
List<@Nullable ? super @NotNull Foo>
List<@Nullable ? super @Nullable Foo>
List<@NotNull ? super @Nullable Foo>
*List<@NotNull ? extends @NotNull Foo>
List<@Nullable ? extends @NotNull Foo>
*List<@Nullable ? extends @Nullable Foo>
List<@NotNull ? extends @Nullable Foo>
* Inconsistent bounds
The text was updated successfully, but these errors were encountered: