Why are the type parameters to List and Map annotated as @NonNull (in FAQ)?

2 views
Skip to first unread message

Piotr Zielinski

unread,
Dec 12, 2009, 1:05:31 PM12/12/09
to checker-framework-discuss
I've read the answer in the FAQ

http://types.cs.washington.edu/checker-framework/current/checkers-manual.html#faq-list-map-nonnull-typeargs

and I don't fully understand the motivation. It seems natural that
the list _implementations_ should impose restrictions on the types
they contain, not the List interface. The same with Map. Am I
missing something here?

A related question: for legacy reasons, Collection<T>.contains() takes
an Object, not T, as an argument. Is there a way to make the checker
permit contains(null) calls on Collection<@Nullable String>, but
reject it on Collection<String> (eg what would I need to write in the
stub file for the Collection class)?

(I'm not suggesting adding anything to the checker framework to deal
with this case, as this is a legacy issue, merely whether the existing
framework can handle this, or maybe the stub processor could be
modified somehow. Low priority though.)

Thanks,
Piotr

Mahmood Ali

unread,
Dec 16, 2009, 3:53:36 PM12/16/09
to checker-framework-discuss
Greetings Piotr,

> It seems natural that
> the list _implementations_ should impose restrictions on the types
> they contain, not the List interface.  The same with Map.  Am I
> missing something here?

A true subclass that satisfies the "is-a" relationship needs to adhere
to the superclass interface: by accepting any value the superclass
accepts. This is known as the Liskov substitution principle (another
way of putting this, A is a subtype of B then every instanceof B can
be replaced with A without changing the correctness of the program).
In other words, an implementation can only relax the parameter
requirements of the superclass, but not restrict it (every equals()
need to handle false, because Object.equals() is specified to accept
null).

Thus, if the List interface specifies to accept null values, so should
*all* the implementation. But that's not the case, unfortunately.

The Collection API doesn't adhere to the subtyping relationships, as
subclasses may throw an Exception optionally for any values (e.g.
handling nulls and read only values).

We are left with two options:
1. Annotate the bound of List as Nullable, and annotate the
collections that don't accept null with bound NonNull. This results
into the subclasses not type-checking properly, but allows the
clients/programmers to use List of nullables a bit easier
2. The otherway (NonNull for List, but Nullable for ArrayList). This
results into the subclasses type-checking properly, but would
inconvenient the clients that desire List of nullables.

We decided to go for the second option as it is more conservative and
safer. However, if we can show that the first option is still safe,
then we can change the current behavior. We may ignore the
unsoundness of the options interaction with raw types for the sake of
discussion.

Regards,
Mahmood
Reply all
Reply to author
Forward
0 new messages