Final means final, doesn’t it?!

For Mike and in memory of good times at ThoughtWorks.

An interface cannot completely dictate an implementation. It’s impossible to stipulate an interface to the point where there is no ambiguity and consequently no opportunity for implementors to produce different somewhat different behaviours. For instance, an interface cannot declare that methods should not accept or return null values.

The set of contractural interfaces that is the JVM specification is certainly no exception.

In recent times I’ve witness nigh-on baffling differences in vendor implementations of the JVM specification.

For example, be cautious with final instance variables. Some implemenations may not make them as final as you’d like.

I was recently involved in a project that employed the persistence services of Hibernate. Originally the team believed we needed domain objects to comply with the bean specification in order for them to be persisted through Hibernate. The consequences were invasive, domain objects that were once clear in purpose, were being polluted by getters, setters and no-arg constructors. Method explosion and confusion reigned.

Then an enlightened team member shed light on the issue – ‘….why not configure hibernate to use direct field access….’. Great! We story carded it and eventually started re-configuring Hibernate and rolling back those invasive domain changes. The changes were completed, tested, and checked in. Shortly there-after cruise control complained – a test stack trace showed evidence of code attempting to modify a final variable.

How could it be?

Once rightfully final instance variables were now being modified post object creation by Hibernate – on developer builds the JVM implementation happened to support modifying final instance variables through reflection, whereas the cruise control JVM did not. Amazing!

In very recent times I’ve also witnessed JVM implementations yield, somewhat less surprisingly, unpredictable behaviour when out of memory. It seems some implemenations don’t always complain about being out of memory, rather they continually throw ClassCastExceptions – suggesting some area of memory has been corrupted.

I’ve also witnessed very different class loading behaviour in JVM’s within application server containers, some being completely unable to locate classes that others are quite capable of loading.

The lesson learned: be clear on the JVM implementation used in production and develop and test against the same instance, or in the absence of a sole concrete instance, test against a variety of candidate implemenations.

Leave a Reply