Set.add
refines the contract of Collection.add
. From the latter's Javadoc:
Collections that support this operation may place limitations on what elements may be added to this collection. In particular, some collections will refuse to add null elements, and others will impose restrictions on the type of elements that may be added. Collection classes should clearly specify in their documentation any restrictions on what elements may be added.
That's what is done in the Javadoc of Set.add
, where it states that e.g. duplicate elements are not added to the set.
Update: on contracts and interfaces
(including and extending my comments below to round out this answer.)
The contract of a method specifies - formally or informally - what the caller is expected to provide as input for that method, and what is the guaranteed outcome of the method call. E.g. the contract may state that no null
parameters are expected, and if the method is passed a null
parameter, it will throw a NullPointerException
. The Javadoc of methods in the Collection framework are good examples of such contracts.
Note that some languages allow or even require the formal definition of contracts, thus the contracts are compiled into the code and actively enforced runtime. Eiffel is such a language. However, Java has no such facility; the contracts defined in Javadoc are not formal, there is not even a strict format defined for them. These are meant only for human reading, left unnoticed by the JVM. Thus, breaking a contract in Java may not be immediately noticeable, only later when the resulting bugs start to appear.
Contracts can be defined both for concrete class methods and abstract/interface methods. The contract of an interface is (should be) binding to all of its implementations. I.e. HashSet.add
, TreeSet.add
, LinkedHashSet.add
etc. all must fulfill the contract of Set.add
(and may further refine it). An implementation which does not behave according to the contract of Set.add
breaks the Liskov substitution principle.