On Sat, Dec 5, 2009 at 2:58 AM, Mahmood Ali <
msae...@gmail.com> wrote:
> I think we could use some feedback on whether it's more desirable to
> have more expressive syntax (i.e. treat <@Nullable T> as <T extends
> @Nullable Object super @Nullable void>) or more compact syntax (i.e.
> <@Nullable T> as <T extends @Nullable Object>).
Ok, now I understand the tradeoff. It seems that the answer may
depend on which of the following two interpretations you adopt.
Interpretation 2 with @PolyNull may be worth looking at.
Interpretation 1 (more or less the current one).
The @Nullable/@NonNull annotations are used to enrich the type system
and then more or less disappear from the picture. For example, the
subtype relationship between @NonNull Object and @Nullable Object is
no more special than that between @NonNull Object and @NonNull String.
In this view, the extends/super mechanism is the primary way of
expressing all type constraints (including nullability). For example,
to express any type, you write <T extends @Nullable Object>. The
meaning of <@Nullable T> or <@NonNull T>, if anything, is just
syntactic sugar for extends/super.
The nice thing about this interpretation is that it reuses the
existing extends/super mechanism to express type constraints.
Unfortunately, the current extends/super syntax in java is not
complete, so some types (eg <T super @Nullable Bottom>) cannot be
expressed (at least not without assigning a special meaning to
<@Nullable T>).
Interpretation 2.
The @Nullable/@NonNull annotations and the standard type system are
orthogonal and handled independently. The extends/super mechanism is
used exclusively for constaints on standard types, and
@Nullable/@NonNull/@PolyNull annotations are used for specifying
nullness. In this interpretation:
<@Nullable T extends String>
T can be @Nullable String but not @NonNull String
<@NonNull T extends String>
T can be @NonNull String but not @Nullable String
<T extends @Nullable String>
doesn't mean anything, extends can only be used on non-annotated types
To express a type that can be both nullable or not, use @PolyNull:
interface Something<@PolyNull T> {
void method1(@PolyNull T object);
void method2(@Nullable T object);
void method3(@NonNull T object);
void method4(T object); // default nullability
}
expands to
interface Something<String> {
void method1(String object);
void method2(@Nullable String object);
void method3(@NonNull String object);
void method4(String object);
}
interface Something<@Nullable String> {
void method1(@Nullable String object);
void method2(@Nullable String object);
void method3(@NonNull String object);
void method4(String object);
}
interface Something<@NonNull String> {
void method1(@NonNull String object);
void method2(@Nullable String object);
void method3(@NonNull String object);
void method4(String object);
}
The nice thing about this interpretation is that generic and concrete
variable declaration (eg 'String obj' and 'T obj') obey the same
default nullability rules. You don't have to check the constraints on
T to find out whether it's nullable or not.
Thoughts?
Piotr