Friday, October 2, 2009

Java Puzzler: enforcing whether a class overrides equals/hashCode


The Problem

Let's say I'm writing a Java class that requires users of my class override equals and hashCode on their objects. I'd be in good company: lots of classes do this, not least the java.util.Collection classes like List and Set.

This important requirement is generally only recorded in the JavaDoc, with a stern warning that Bad Things Will Happen if you forget. But the requirement is not enforced at runtime, much less compile time.

The Question

What if I wanted to enforce it? What if I wanted to write a class like HashMap that made sure anything you put in it overrode equals and hashCode? What if I'm prepared to sacrifice a little performance and/or code complexity to do this? What are my choices? Can I do it at runtime? Better yet, can I do it at compile-time?

The Answer?

I don't know a great answer. I don't know of any libraries that do. I'd love some feedback on helping eliminate this important category of subtle bugs.

To get the ball rolling, this is what I'm thinking of doing in Metawidget:

Class classToTest = objectToCache.getClass();
Object dummy1 = classToTest.newInstance();
Object dummy2 = classToTest.newInstance();

if ( !dummy1.equals( dummy2 ))
throw new Exception( classToTest + " does not override .equals(), so cannot be reliably cached" );

if ( dummy1.hashCode() != dummy2.hashCode() )
throw new Exception( classToTest + " does not override .hashCode(), so cannot be reliably cached" );

This approach takes advantage of the fact that, given your class has internal state, two brand new instances should always have equivalent state, but the default Object.equals (which uses ==) will return false. It's not perfect. It'll work if you write a POJO and forget to override equals. But it won't work if your superclass overrides equals but your subclass, which adds some more internal state, forgets to. But it's a start.

Note that, annoyingly, this doesn't seem to work:

class.getDeclaredMethod( "hashCode" )

Public methods like hashCode and equals are always considered 'declared methods', even if the class doesn't actually declare them. Similarly:

class.getDeclaredMethod( "hashCode" ).getDeclaringClass()

Always returns the subclass name, even if the subclass doesn't override hashCode.

Suggestions welcome!

Update

Thanks to everyone for the really helpful comments. The first thing to note is that...

class.getDeclaredMethod( "hashCode" ).getDeclaringClass()

...does actually work. I don't know why it didn't seem to when I tried it originally. Evidentally I am an idiot!

Anyway, here's what's currently going into Metawidget, based on everyone's feedback:

Class<?> configClass = configToStoreUnder.getClass();

// Hard error

// equals

Class<?> equalsDeclaringClass = configClass.getMethod( "equals", Object.class ).getDeclaringClass();

if ( Object.class.equals( equalsDeclaringClass ) )
throw new Exception( configClass + " does not override .equals(), so cannot cache reliably" );

// hashCode

Class<?> hashCodeDeclaringClass = configClass.getMethod( "hashCode" ).getDeclaringClass();

if ( Object.class.equals( hashCodeDeclaringClass ) )
throw new Exception( configClass + " does not override .hashCode(), so cannot cache reliably" );

if ( !equalsDeclaringClass.equals( hashCodeDeclaringClass ) )
throw new Exception( equalsDeclaringClass + " implements .equals(), but .hashCode() is implemented by " + hashCodeDeclaringClass + ", so cannot cache reliably" );

if ( !configClass.equals( equalsDeclaringClass ) )
{
// Soft warning
//
// Note: only show this if the configClass appears to have its own 'state'.
// Base this assumption on whether it declares any methods. We don't want to
// use .getDeclaredFields because that requires a security manager
// check of checkMemberAccess(Member.DECLARED), whereas we may only have
// checkMemberAccess(Member.PUBLIC) permission

for ( Method declaredMethod : configClass.getMethods() )
{
if ( configClass.equals( declaredMethod.getDeclaringClass() ) )
{
LOG.warn( configClass + " does not override .equals() (only its super" + equalsDeclaringClass + " does), so may not be cached reliably" );
break;
}
}

// Note: not necessary to do !configClass.equals( hashCodeDeclaringClass ),
// as will already have thrown an Exception from
// !equalsDeclaringClass.equals( hashCodeDeclaringClass ) if that's the case
}

Improvements welcome!

Thursday, September 17, 2009

Metawidget Elevator Pitch


I've been having fun lately drawing an Elevator Pitch for Metawidget.

I was inspired by the style of the Google Chrome Book, though clearly I'm no Scott McCloud!

Check it out here: http://metawidget.org/elevator.html

As always, your feedback is much appreciated.

Tuesday, August 11, 2009

GWT Metawidget takes a walk on the client side

The latest release of Metawidget upgrades our GWT support to 1.7 and includes a new example of running pure client-side GWT:

By default, GwtMetawidget inspects business objects server-side. This is because client-side JavaScript does not support reflections or annotations.

However if you don't need reflections or annotations, and have your own way of retrieving inspection results, you can plug in your own Inspector and keep everything client-side. This example retrieves inspection results from a textarea and generates the UI.

Download the example here. More documentation can be found here.

Metawidget on ICE

The latest release of Metawidget includes support for the ICEfaces component library.

Metawidget's philosophy of not 'owning' the UI, of integrating with existing UI frameworks and component libraries, means it can easily take advantage of awesome component libraries such as ICEfaces and all the AJAX-goodness they provide.

The Metawidget distribution includes an example of Metawidget and ICEfaces working together to deliver a rich, AJAX-driven UI with minimal code:


All the input boxes and command buttons in the screenshot are generated at runtime by Metawidget, and update dynamically using AJAX.

Download the example here. More documentation can be found here.

AJAX User Interface Generator: Metawidget v0.8

Version 0.8 of Metawidget, the AJAX user interface generator, is now available. This release includes:Special thanks to Ted Goddard for his help with this release!

As always, the best place to start is the Reference Documentation:


Your continued feedback is invaluable to us. Please download it and let us know what you think.

Monday, August 3, 2009

Geek mid-life crisis

I recently completed my 'geek mid-life crisis' and joined the ranks of those who attempt to recapture their youth and relive by-gone days by building their own arcade cabinet.

I was inspired by watching The King of Kong and considered purchasing a professional cabinet, but installing something bigger than a fridge freezer in the living room is a tough sell for a family home: I needed something more slimline and less obtrusive, which is when I came across the awesome job this guy had done, so I decided to have a go myself.

Here are the initial plans (they changed a little bit during construction):


The work in progress (this was my first time with a router, or even a drill for that matter, so I asked a grown up to help me):


And the finished product (a few months later):



Costs (in Aussie dollars):
  • Joystick and Encoder board (incl shipping) - $203.85 - Replay Arcade

  • 20" L200P LCD monitor - $172 (incl shipping) - eBay

  • Coin buttons - $18 (incl shipping) - OzStick

  • Wood - $120 - Bill's boards

  • Artwork - $170.50 (incl shipping) - MAME Marquees

  • 1L Black paint - $30

  • 6mm laminated glass - $40 - JB Glass

  • 2 perspex sheets - $20

  • 2.8GHz 1GB P4 PC - free (a local school was getting rid of one)
All up about $800 Aussie dollars, plus a bunch of sweat and tears and, yes, even blood (I cut myself a couple times). Sincere thanks to the many people who either helped or were inconvenienced during this hair-brained project!

Saturday, August 1, 2009

Home office

I thought I'd post a couple snaps of where I've been spending my working day for the last 18 months:


I'm a big advocate of multiple screens: two is definitely better than one; after a while with two you start thinking you need a third; and if you go three you need 2 video cards so you may as well have four! What do I use four screens for?
  • Far left: e-mail, reading PDFs of specifications etc

  • Middle left: running version of whatever application I'm developing

  • Middle right: my IDE, spread over two screens, with this screen being a full screen of source code (bliss :)

  • Far right: the rest of my IDE, including server console, debugging tree, folder navigator etc
It took a little while to get used to, but now I'd never want to go back.