UnsupportedOperationException - a necessary evil?


  • 1

    In the Java world, we often run into an UnsupportedOperationException when we deal with certain class hierarchies. It manifests when an implementation of an interface method (abstract method) does not want to implement that method! This is often outrageous as the client of the super type has no idea that the operation is not supported by one of its subtypes which may cause it to fail unexpectedly - a blatant violation of The Principle of Least Surprise!

    interface A {
    	void f();
    }
    
    class AImpl implements A {
    	void f() {
    		throw new UnsupportedOperationException();
    	}
    }
    
    class AClient {
    	void doStuff(A a) { 
    		a.f(); //if AImpl is passed in here, we will encounter the exception!
    	}
    }
    

    Why then does the subtype agree (by the implements/extends keyword) to implement the method in the first place? We find this typically in wide interfaces (ones with lots of operations) such as that of Collection where certain operations may not apply to all subtypes. As is stated in the official API doc, certain implementations of Collection (immutable collections) may actually not support Collection.remove() or even Collection.add() for instance (eg. Collections.emptyList() returns such a collection). In such cases, these methods throw this exception.
    Is there a way around this? What if we argue that wide interfaces aren't desirable and to do away with them? Given below are some ways:

    1. We could have two different inheritance trees with narrower bases : one for MutableCollection and the other for ImmutableCollection. This would lead to loads of classes/interfaces such as MutableList, ImmutableList, MutableMap, ImmutableMap and so on. Now this is just one dimension : immutability. What if we want to add a dimension : tolerance for nulls?! Going down this road we would end up with even more classes and interfaces! (MutableListWithNull, MutableListWithoutNull, ......) The no of classes/interfaces will increase exponentially with each dimension! That is some nightmare!
      Also, what if some client code wants to use a functionality generic to both inheritance trees described above? Say a client simply wants to iterate over a list. Now the client is forced to have two code sections to process separately a MutableList and an ImmutableList -> a complete annihilation of polymorphism as now the client must know about subtype details it does not care about!

    2. We could leave the implementations of non-applicable methods blank in the subclasses! Great! No exception! Isn't this better? Well its a hell of a lot worse! This is an even worse violation of The Principle of Least Surprise! Here the client would not even be notified that the operation failed!

    Therefore, in some cases the supposedly evil UnsupportedOperationException is necessary!


Log in to reply
 

Looks like your connection to LeetCode Discuss was lost, please wait while we try to reconnect.