rev_dihazum
rev_dihazum

Reputation: 818

How to disable clear functionality of Java List

I have a static List property (e.g. ArrayList<String>) which is passed among multiple methods to add/modify elements. There are already few methods and in near future many methods to come.

So I need to ensure that no method could clear the list. So far I have overridden clear() method with empty method body.

My approach is as bellow:

private static List<String> singleList = new ArrayList<String>(){
    @Override
    public void clear() {
        //here I disabled clear() with empty method 
    };
};

public static void main(String[] args) throws IOException {
    addNumber(singleList);
    addDigit(singleList);
    addSign(singleList);
    System.out.println(singleList);
}


private static void addNumber(List<String> singleList) {
    singleList.add("1");
    singleList.add("2");
}

private static void addDigit(List<String> singleList) {
    singleList.add("A");
    singleList.add("B");
    singleList.clear();// Suppose, this line wrote accidentally, and I need to prevent it.
}

private static void addSign(List<String> singleList) {
    singleList.add("+");
    singleList.add("/");
}

Is there any better option to do that?

Please suggest me.

Upvotes: 0

Views: 465

Answers (5)

Ioan
Ioan

Reputation: 5187

For this purpose, you could use immutability. Instead of mutating the list, just return always an immutable one with the elements you want to have. You could use, for example, Guava ImmutableList, create a builder and add the previous list and/or new elements.

Guava docs about the clear method:

Guaranteed to throw an exception and leave the collection unmodified.

Upvotes: 3

Andy Turner
Andy Turner

Reputation: 140318

A better approach than extending ArrayList - although more verbose - is to compose ArrayList with a class which prevents clearing.

Basically, create a class which implements List, and delegates all of its method calls to another List instance, other than clear:

final class CannotClearList<E> implements List<E> {
  private final List<E> delegate;

  CannotClearList(List<E> delegate) {
    this.delegate = delegate;
  }

  @Override public boolean add(E element) {
    return delegate.add(element);
  }

  @Override public E get(int i) {
    return delegate.get(i);
  }

  @Override public void clear() {
    // Cannot "do nothing": that violates the definition of the
    // Collection.clear() method.
    throw new UnsupportedOperationException();
  }

  // etc, for all other methods.
}

Aside from the advantage that you aren't now restricted to having an ArrayList that you cannot clear (you could reuse it e.g. for a LinkedList), the real advantage in doing this is that in order to prevent clear() from working, you also have to prevent lists returned by List.subList from having clear() called on them.

If you have prevented clear() by extending ArrayList, you would need to then create another class which prevents clear() on the sublist also. However, with CannotClearList above, you can simply wrap the sublist in another CannotClearList:

@Override public List<E> subList(int from, int to) {
  return new CannotClearList<>(delegate.subList(from, to));
}

Note that clear() is not the only way to clear a list. You can also:

  • Repeatedly call remove;
  • Get an Iterator or ListIterator and call remove() whilst iterating that;
  • Calling retainAll(Collection<?>) with an empty collection (or a collection with no elements in common) as the argument.

You should consider carefully whether you also want to prevent these cases.

Upvotes: 2

niilzon
niilzon

Reputation: 426

You can create your own UnClearableList which extends ArrayList - then in UnClearableList simply override the clear() method and do nothing in it (or possibly throw an exception, depending on your design).

Then use the UnClearableList when you are in such "I want to allow add / remove but no clear of my lists" cases.

Your way of doing it is technically correct. Doing this with a special UnClearableList lets you reuse it in other classes. If this is just a school exercise then it is not really necessary.

Upvotes: 0

Peter Lawrey
Peter Lawrey

Reputation: 533530

I would add an UnsupportedOperationException to make sure the code which does call clear() is corrected.

If the code relies on clear() working, it shouldn't be using that collection.

If the code doesn't need clear() it shouldn't be calling it.

Upvotes: 2

samjaf
samjaf

Reputation: 1053

Creating a new type or overloading an existing one (as you did) is the usual approach. Furthermore the interface List explicitly allows disabling clear as it is an optional operation.

From the JavaDoc:

/**
 * Removes all of the elements from this list (optional operation).
 * The list will be empty after this call returns.
 *
 * @throws UnsupportedOperationException if the <tt>clear</tt> operation
 *         is not supported by this list
 */
void clear();

Upvotes: 0

Related Questions