His conclusion is that we should simply not be marking classes as final because it really doesn’t bring any benefits. Even if you’re trying to follow Design by Extension, the next guy is just going to remove the final keyword if it suits his purposes.
Sadly, I suspect he’s right. (Although there’s a conversation to be had here around collective code ownership, and assuming the last person did the best possible job. i.e. that final keyword is maybe there for a reason.)
However, things get a lot less clear cut when dealing with libraries. Take, for example, Apache Ant. I’m writing a custom executor for Ant (Parallel Ant). I (mostly!) did the development on this project using TDD, but mocking out the Ant Project class so I could verify my interactions with it was, well, interesting. This particular class is not final, so the JMock ClassImposteriser should do the job nicely. However, some of the methods I wanted to call are marked final. So I’m back to square one. Only now, things are actually worse, because there’s no interface I can use in place of the class. So I can’t directly test my interactions with Project (or Target for that matter).
What’s particularly odd here is that across all of the methods on those two classes, there are only five final methods — three if your discount overloaded forms — and I wanted to call/mock two of them!
I think it’s pretty clear from looking at the Project class, that it is not expected to be extended. Of the 103 methods on this class, only four are marked final. One has to wonder why? Why those four and none of the others? This class is not designed for extension, but there is one subclass — MockProject, which lives in one of the unit tests. This is clearly a terrible smell.
But, smell aside, as a user of this library, what am I now supposed to do? The class is not final, so I can mock it. But I can’t intercept the methods I care about, because they are final. And there’s no interface to use as a proxy for the class. What I ended up doing, was writing a very simple class (AntWrapper) which delegates to the various final methods. My code interacts with this class (via an interface! Sorry, Adrian!) and I can verify my interactions, etc. However, the AntWrapper class itself is still untestable. Hopefully, the trusty old Mk I Eyeball should pick up bugs in this class.
I don’t feel like I’ve made progress here. If this was code I had collective ownership of, I’d probably just remove the final and move on. See, Adrian was right. But that’s still not really the right answer in my mind.