Java Deep

Pure Java, what else

Monthly Archives: October 2014

Do not use immutable in your API

Why should an API define methods that accept Guava Immutable collection types? It should not. The intent of the author of such an API is clear: she wants to declare and be safe that the method does not modify the collection that the caller passes. The problem is that it forces the caller to use Guava immutable collections and the caller can not just pass a hash or tree map, a hash set or whatever is a matching type for the actual collection. I know that this is not a big deal to convert a map or set to the immutable counterpart, but let me, as a user of a library have the freedom to decide if I want to do that.

It does not mean that I do not need guarantee. When an API documents that a collection that the caller passes will not be altered, I expect the implementation (a.k.a. library) not to do that. I expect it working just like any other feature, which is documented. However, the implementation is the internal responsibility of the library and has to be well coded during development time. It simply should not call any of the mutating methods. To assess that the development time tools have to be used. Unit tests. Code analysis.

If the library wants to be on the safe side, it can use the Guava library to “copy” the value passed as argument to the API from a collection interface to the appropriate immutable implementation. Or it can use the JDK built in Collections.unmodifiableXXX methods. Whether it does it or not is implementation detail. The API, the declaration of the classes, methods, arguments are the “interface”. The API user should only face the interface and not the implementation details.

Therefore I say: do not use immutable implementations in the method parameter list. Use them in the implementation only.

Why to use String

Recently I was tutoring juniors during a training session. One of the task was to write a class that can dwarwle maps based on some string key. The result one of the juniors created contained the following method:

	void dwarwle(HashMap<String,Dwarwable> mapToDwarwle, String dwarwleKey){
		for( final Entry<String, Dwarwable> entry : mapToDwarwle.entrySet()){
			dwarwle(entry.getKey(),entry.getValue(),dwarwleKey);
		}
	}

The code was generally ok. The method to dwarwle an individual dwarwable entry using the actual key it is assigned to in the hash map and the dwarwle key is factored to a separate method. It is so simple that I do not list here. The variable names are also meaningful so long as long you know what actually dwarwling is. The method is short and readable, but the argument list expects a HashMap instead of a Map. Why do we want to restrict the caller to use a HashMap? What if the caller has a TreeMap and for good reason. Do we want to have separate method that can dwarwle TreeMap? Certainly not.

Expect the interface, pass the implementation.

The junior changed the code replacing HashMap to Map but after five minutes or so this clever lady raised her hand and had the following question:

“If we changed HashMap to Map, why did not we change String to CharSequence?”

It is not so easy to answer a question like that when it comes out of the blue. The first thing that came up in my mind is that the reason is that we usually do it that way and that is why. But that is not a real argument, at least I would not accept anything like that and I also expect my students not to accept such answer. It would be very dictator style anyway.

The real answer is that the parameter is used as a key in a map and the key of a map should be immutable (at least mutation should be resilient to equals and hashcode calculation). CharSequence is an interface and an interface in Java (unfortunately) can not guarantee immutability. Only implementation can. String is a good, widely known and well tested implementation of this interface and therefore can be a good choice. There is a good discussion about it on stackoverflow.

In this special case we expect the implementation because we need something immutable and we “can not” trust the caller to pass a character sequence implementation that is immutable. Or: we can, but it has a price. If a StringBuilder is passed and modified afterwards our dwarwling library may not work and a blame war may start. When we design an API and a library we should also think about not only the possible but also about the actual, average use.

A library is as good as it is used and not as it could be used.

This can also be applied to other products, not only libraries but this may lead too far (physics and weapon).