Saturday, September 4, 2010

Why I don't like Java's finals

Although I favor immutability over mutability, I must say that I don't like Java's finals. The intention when introducing it was definitely great and good, but I think it does more harm than provides benefits.

Consider the following.

Finals prevent you from mocking classes.
Yes, they do. I hardly know any framework that is capable of mocking class that is final; you can play with some workarounds like unfinalizers or framework extensions to do the trick, but it comes for the price of a test / test set-up complication (at least) and an additional cost (running time, introduction time).

There are people who suggest that you don't need to mock classes for your tests, but I hardly find it acceptable: imagine yourself writing unit tests for class that uses a "final" server as a parameter:

class LogCollector {
   public LogCollector(LogServer server) {
     // whatever
   } 
}


I personally prefer to use fine-grained tests before running large test routines (requiring real server instance set up, for example), but given the server is final, I have no choice - I am left with large tests, with all their costs.

Finals lie to you.
Consider the following code:

final Set customers;

It's true, that once assigned, the customers set cannot be assigned once again, but you can still modify the set anyway - it's a matter of calling customers.add(Customer). Unless you're using an UnmodifiableSet, nothing prevents you from changing it's contents.

Finals force you to predict future.
I'm not much of a subclassing fan, but it is understandable to subclass something from time to time to enhance a bit the functionality already given. With finals, you can't do that by design. Thus, you have to predict the possible future use of your objects - and I feel no one was able to do that for something more complicated than a primitive type.

Disabling subclassing may also be a pain in an inflexible type system like Java's:

class NoisyCar {
   void run() {
     System.out.printl("BRRRRRRUUUUUUM");
   }

   // other methods 
}



class QuietCar {
   void run() {
     System.out.printl("brrrrrruuuuuuum");
   }

   // other methods, same as in Car 
}

X rentedCar = carRentan.getAvailableCar(); // We don't know what do they have available, but we need a car immediately

What's the type X?

Possible solution 0: Ducktyping. Not in Java.

Possible solution 1: We could use an interface Car, but then we get some serious code duplication (we would have to either copy-paste other methods or refactor out common code to a separate class, which may - in this case - seriously complicate your life).

Workaround: If we had functions as first-class values, we could probably come up with not a bad workaround and carry on with finals, but... well, we don't have them in Java.

Possible solution 2: Allow removing final, introduce a superclass and let NoisyCar and QuietCar subclass it.

Any other ideas?

Finals prevent you from flexible debugging.
I've found out that recently: while debugging, you cannot change the value of the final variable (it's not a surprise, though). What's the problem? You cannot experiment - while debugging, you cannot change the value of an interesting variable and thus you're sticked to a certain path of execution. Bad, if you encounter it in real life.

Finals encourage bad practices.
Namely, they encourage you to expose fields of a class. Since no one can change them, what's the big deal? Well, there is. You're explicitly saying the class users: this field is publicly available. You can use it. Once you change your mind and, for example, decide to provide an active getter (fetching missing data, for example) with some decoration around it (like sanity checks) you can't - someone uses the field already. And you cannot deprecate a field to let users know it's going to be gone, can you?

On one hand, it's bad to create not-fully-initialized objects. On the other, lazy evaluation may be a great optimization. It's all up to the case you're dealing with. Will finals help you? Well, they limit your flexibility and let the implementation leak, so you may have trouble.


That's my 2 cents. Please read both why finals should be deprecated and why they shouldn't and make up your mind. In my personal opinion, they rarely are worth using.

Edit: there is some follow up on this post here.