Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8313649

Collections mutator methods should all be marked as optional operations

    XMLWordPrintable

Details

    • CSR
    • Resolution: Approved
    • P3
    • 22
    • core-libs
    • None
    • behavioral
    • minimal
    • Java API
    • SE

    Description

      Summary

      The (optional operation) marking is missing from several methods in the collections framework.

      Problem

      The collections framework permits interfaces to be unmodifiable and thus allows mutator methods to be marked (optional operation). This marking is missing from several mutator methods.

      A class that inherits an interface with such methods is permitted to throw UnsupportedOperationException instead of performing the requested operation. The "throws" documentation for some of these methods is missing.

      Additionally, the "throws UnsupportedOperationException" clauses of some of these methods are poorly worded, referring to specifics of the default implementation.

      Solution

      Add the (optional operation) marking to methods where it is missing, and add the "throws UnsupportedOperationException" clause to several methods. As necessary, reword this throws clause to use the standard form, which is along the lines of "throws UnsupportedOperationException if the operation is not supported by this ."

      Specification

      java.util.Collection.removeAll():

            * @param c collection containing elements to be removed from this collection
            * @return {@code true} if this collection changed as a result of the
            *         call
      -     * @throws UnsupportedOperationException if the {@code removeAll} method
      +     * @throws UnsupportedOperationException if the {@code removeAll} operation
            *         is not supported by this collection
            * @throws ClassCastException if the types of one or more elements
            *         in this collection are incompatible with the specified

      java.util.Collection.removeIf():

           /**
            * Removes all of the elements of this collection that satisfy the given
      -     * predicate.  Errors or runtime exceptions thrown during iteration or by
      -     * the predicate are relayed to the caller.
      +     * predicate (optional operation).  Errors or runtime exceptions thrown during
      +     * iteration or by the predicate are relayed to the caller.
            *
            * @implSpec
            * The default implementation traverses all elements of the collection using
      ...
            *        removed
            * @return {@code true} if any elements were removed
            * @throws NullPointerException if the specified filter is null
      -     * @throws UnsupportedOperationException if elements cannot be removed
      -     *         from this collection.  Implementations may throw this exception if a
      -     *         matching element cannot be removed or if, in general, removal is not
      -     *         supported.
      +     * @throws UnsupportedOperationException if the {@code removeIf} operation
      +     *         is not supported by this collection
            * @since 1.8
            */
           default boolean removeIf(Predicate<? super E> filter) {

      java.util.List.replaceAll():

           /**
            * Replaces each element of this list with the result of applying the
      -     * operator to that element.  Errors or runtime exceptions thrown by
      -     * the operator are relayed to the caller.
      +     * operator to that element (optional operation).  Errors or runtime
      +     * exceptions thrown by the operator are relayed to the caller.
            *
            * @implSpec
            * The default implementation is equivalent to, for this {@code list}:
      ...
            * replacing the first element.
            *
            * @param operator the operator to apply to each element
      -     * @throws UnsupportedOperationException if this list is unmodifiable.
      -     *         Implementations may throw this exception if an element
      -     *         cannot be replaced or if, in general, modification is not
      -     *         supported
      +     * @throws UnsupportedOperationException if the {@code replaceAll} operation
      +     *         is not supported by this list
            * @throws NullPointerException if the specified operator is null or
            *         if the operator result is a null value and this list does
            *         not permit null elements

      java.util.List.sort():

           /**
            * Sorts this list according to the order induced by the specified
      -     * {@link Comparator}.  The sort is <i>stable</i>: this method must not
      -     * reorder equal elements.
      +     * {@link Comparator} (optional operation).  The sort is <i>stable</i>:
      +     * this method must not reorder equal elements.
            *
            * <p>All elements in this list must be <i>mutually comparable</i> using the
            * specified comparator (that is, {@code c.compare(e1, e2)} must not throw
      ...
            *          {@linkplain Comparable natural ordering} should be used
            * @throws ClassCastException if the list contains elements that are not
            *         <i>mutually comparable</i> using the specified comparator
      -     * @throws UnsupportedOperationException if the list's list-iterator does
      -     *         not support the {@code set} operation
      +     * @throws UnsupportedOperationException if the {@code sort} operation
      +     *         is not supported by this list
            * @throws IllegalArgumentException
            *         (<a href="Collection.html#optional-restrictions">optional</a>)
            *         if the comparator is found to violate the {@link Comparator}

      java.util.Map.replaceAll():

           /**
            * Replaces each entry's value with the result of invoking the given
            * function on that entry until all entries have been processed or the
      -     * function throws an exception.  Exceptions thrown by the function are
      -     * relayed to the caller.
      +     * function throws an exception (optional operation). Exceptions thrown
      +     * by the function are relayed to the caller.
            *
            * @implSpec
            * <p>The default implementation is equivalent to, for this {@code map}:
      ...
            * concurrency properties.
            *
            * @param function the function to apply to each entry
      -     * @throws UnsupportedOperationException if the {@code set} operation
      -     *         is not supported by this map's entry set iterator.
      +     * @throws UnsupportedOperationException if the {@code replaceAll} operation
      +     *         is not supported by this map
      +     *         ({@linkplain Collection##optional-restrictions optional})
            * @throws ClassCastException if the class of a replacement value
            *         prevents it from being stored in this map
            *         ({@linkplain Collection##optional-restrictions optional})

      java.util.Map.putIfAbsent():

           /**
            * If the specified key is not already associated with a value (or is mapped
            * to {@code null}) associates it with the given value and returns
      -     * {@code null}, else returns the current value.
      +     * {@code null}, else returns the current value (optional operation).
            *
            * @implSpec
            * The default implementation is equivalent to, for this {@code map}:
      ...
            *         (A {@code null} return can also indicate that the map
            *         previously associated {@code null} with the key,
            *         if the implementation supports null values.)
      -     * @throws UnsupportedOperationException if the {@code put} operation
      +     * @throws UnsupportedOperationException if the {@code putIfAbsent} operation
            *         is not supported by this map
            *         ({@linkplain Collection##optional-restrictions optional})
            * @throws ClassCastException if the key or value is of an inappropriate

      java.util.Map.remove(key, value):

           /**
            * Removes the entry for the specified key only if it is currently
      -     * mapped to the specified value.
      +     * mapped to the specified value (optional operation).
            *
            * @implSpec
            * The default implementation is equivalent to, for this {@code map}:

      java.util.Map.replace(key, oldValue, newValue):

           /**
            * Replaces the entry for the specified key only if currently
      -     * mapped to the specified value.
      +     * mapped to the specified value (optional operation).
            *
            * @implSpec
            * The default implementation is equivalent to, for this {@code map}:
      ...
            * @param oldValue value expected to be associated with the specified key
            * @param newValue value to be associated with the specified key
            * @return {@code true} if the value was replaced
      -     * @throws UnsupportedOperationException if the {@code put} operation
      -     *         is not supported by this map ({@linkplain Collection##optional-restrictions optional})
      +     * @throws UnsupportedOperationException if the {@code replace} operation
      +     *         is not supported by this map
      +     *         ({@linkplain Collection##optional-restrictions optional})
            * @throws ClassCastException if the class of a specified key or value
            *         prevents it from being stored in this map
            * @throws NullPointerException if a specified key or newValue is null,

      java.util.Map.replace(key, value):

           /**
            * Replaces the entry for the specified key only if it is
      -     * currently mapped to some value.
      +     * currently mapped to some value (optional operation).
            *
            * @implSpec
            * The default implementation is equivalent to, for this {@code map}:
      ...
            *         (A {@code null} return can also indicate that the map
            *         previously associated {@code null} with the key,
            *         if the implementation supports null values.)
      -     * @throws UnsupportedOperationException if the {@code put} operation
      +     * @throws UnsupportedOperationException if the {@code replace} operation
            *         is not supported by this map
            *         ({@linkplain Collection##optional-restrictions optional})
            * @throws ClassCastException if the class of the specified key or value

      java.util.Map.computeIfAbsent():

           /**
            * If the specified key is not already associated with a value (or is mapped
            * to {@code null}), attempts to compute its value using the given mapping
      -     * function and enters it into this map unless {@code null}.
      +     * function and enters it into this map unless {@code null} (optional operation).
            *
            * <p>If the mapping function returns {@code null}, no mapping is recorded.
            * If the mapping function itself throws an (unchecked) exception, the
      ...
            * @throws NullPointerException if the specified key is null and
            *         this map does not support null keys, or the mappingFunction
            *         is null
      -     * @throws UnsupportedOperationException if the {@code put} operation
      -     *         is not supported by this map
      -     *         ({@linkplain Collection##optional-restrictions optional})
      +     * @throws UnsupportedOperationException if the {@code computeIfAbsent} operation is not
      +     *         supported by this map ({@linkplain Collection##optional-restrictions optional})
            * @throws ClassCastException if the class of the specified key or value
            *         prevents it from being stored in this map
            *         ({@linkplain Collection##optional-restrictions optional})

      java.util.Map.computeIfPresent():

           /**
            * If the value for the specified key is present and non-null, attempts to
      -     * compute a new mapping given the key and its current mapped value.
      +     * compute a new mapping given the key and its current mapped value
      +     * (optional operation).
            *
            * <p>If the remapping function returns {@code null}, the mapping is removed.
            * If the remapping function itself throws an (unchecked) exception, the
      ...
            * @throws NullPointerException if the specified key is null and
            *         this map does not support null keys, or the
            *         remappingFunction is null
      -     * @throws UnsupportedOperationException if the {@code put} operation
      -     *         is not supported by this map
      -     *         ({@linkplain Collection##optional-restrictions optional})
      +     * @throws UnsupportedOperationException if the {@code computeIfPresent} operation is not
      +     *         supported by this map ({@linkplain Collection##optional-restrictions optional})
            * @throws ClassCastException if the class of the specified key or value
            *         prevents it from being stored in this map
            *         ({@linkplain Collection##optional-restrictions optional})

      java.util.Map.compute():

           /**
            * Attempts to compute a mapping for the specified key and its current
      -     * mapped value (or {@code null} if there is no current mapping). For
      -     * example, to either create or append a {@code String} msg to a value
      -     * mapping:
      +     * mapped value, or {@code null} if there is no current mapping (optional
      +     * operation). For example, to either create or append a {@code String}
      +     * msg to a value mapping:
            *
            * <pre> {@code
            * map.compute(key, (k, v) -> (v == null) ? msg : v.concat(msg))}</pre>
      ...
            * @throws NullPointerException if the specified key is null and
            *         this map does not support null keys, or the
            *         remappingFunction is null
      -     * @throws UnsupportedOperationException if the {@code put} operation
      -     *         is not supported by this map
      -               ({@linkplain Collection##optional-restrictions optional})
      +     * @throws UnsupportedOperationException if the {@code compute} operation is not
      +     *         supported by this map ({@linkplain Collection##optional-restrictions optional})
            * @throws ClassCastException if the class of the specified key or value
            *         prevents it from being stored in this map
            *         ({@linkplain Collection##optional-restrictions optional})

      java.util.Map.merge():

           /**
            * If the specified key is not already associated with a value or is
      -     * associated with null, associates it with the given non-null value.
      -     * Otherwise, replaces the associated value with the results of the given
      -     * remapping function, or removes if the result is {@code null}. This
      +     * associated with null, associates it with the given non-null value (optional
      +     * operation). Otherwise, replaces the associated value with the results of
      +     * the given remapping function, or removes if the result is {@code null}. This
            * method may be of use when combining multiple mapped values for a key.
            * For example, to either create or append a {@code String msg} to a
            * value mapping:
      ...
            *        present
            * @return the new value associated with the specified key, or null if no
            *         value is associated with the key
      -     * @throws UnsupportedOperationException if the {@code put} operation
      -     *         is not supported by this map
      -     *         ({@linkplain Collection##optional-restrictions optional})
      +     * @throws UnsupportedOperationException if the {@code merge} operation is not
      +     *         supported by this map ({@linkplain Collection##optional-restrictions optional})
            * @throws ClassCastException if the class of the specified key or value
            *         prevents it from being stored in this map
            *         ({@linkplain Collection##optional-restrictions optional})

      java.util.NavigableMap.pollFirstEntry():

           /**
            * Removes and returns a key-value mapping associated with
      -     * the least key in this map, or {@code null} if the map is empty.
      +     * the least key in this map, or {@code null} if the map is empty
      +     * (optional operation).
            *
            * @return the removed first entry of this map,
            *         or {@code null} if this map is empty
      +     *
      +     * @throws UnsupportedOperationException if the {@code pollFirstEntry}
      +     *         operation is not supported by this map
            */
           Map.Entry<K,V> pollFirstEntry();

      java.util.NavigableMap.pollLastEntry():

           /**
            * Removes and returns a key-value mapping associated with
      -     * the greatest key in this map, or {@code null} if the map is empty.
      +     * the greatest key in this map, or {@code null} if the map is empty
      +     * (optional operation).
            *
            * @return the removed last entry of this map,
            *         or {@code null} if this map is empty
      +     *
      +     * @throws UnsupportedOperationException if the {@code pollLastEntry}
      +     *         operation is not supported by this map
            */
           Map.Entry<K,V> pollLastEntry();

      java.util.NavigableSet.pollFirst():

           /**
            * Retrieves and removes the first (lowest) element,
      -     * or returns {@code null} if this set is empty.
      +     * or returns {@code null} if this set is empty (optional operation).
            *
            * @return the first element, or {@code null} if this set is empty
      +     *
      +     * @throws UnsupportedOperationException if the {@code pollFirst}
      +     *         operation is not supported by this collection
            */
           E pollFirst();

      java.util.NavigableSet.pollLast():

           /**
            * Retrieves and removes the last (highest) element,
      -     * or returns {@code null} if this set is empty.
      +     * or returns {@code null} if this set is empty (optional operation).
            *
            * @return the last element, or {@code null} if this set is empty
      +     *
      +     * @throws UnsupportedOperationException if the {@code pollLast}
      +     *         operation is not supported by this collection
            */
           E pollLast();

      Attachments

        Issue Links

          Activity

            People

              smarks Stuart Marks
              smarks Stuart Marks
              Brian Burkhalter, Naoto Sato
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: