Wednesday, March 03, 2010

Scala features for Java Programmers: Case Classes

I'm a Java programmer who's been casually evaluating Scala for about 6 months.  I find it very hard to objectively compare the two languages.  Would concise collection literals save me significant time every day?  Do boilerplate getter methods really ever cause bugs, or do they just offend my sense of style? In what ways can Java be improved to allow me to write better code? What Java-related factors cause me to waste the most time, day-to-day?

I think that's a very difficult question to answer.  So far, I've come up with three principles to help me evaluate various differences between languages:
1. Would it make my code easier to read?
2. Would I be able to more easily write correct code?
3. Does it addresses common problems I've encountered that are related to Java?

For my first comparison, can you quickly tell me what the bug is here:

Did you catch the bug?  The problem is I had Eclipse generate hashCode/equals before I added the "_firstName" field.  This mistake is very easy to make, and can be brutal to track down in a bigger system.  Here's an example of how things break when you screw up hashCode:

I feel this is an example of where another language may make it easier to write this code correctly.  At 96 lines to essentially represent a struct of 5 fields, I'd say another language has ample opportunity to help me make this code more readable.  These type of "Bean" objects - heavy on data, light on code, are so common in Java, language level support seems justifiable.

One of my favorite Scala features addresses the readability and correctness problems mentioned above: Case Classes.  Case Classes build on Scala's already succinct class definition format, and adds some extra goodies such as compiler-generated equals and hashCode methods.  Of course, compiler-generated means that as you add/subtract fields, hashCode and equals automatically adjust accordingly.

I know it seems small, having the compiler maintain hashCode and equals methods on "Bean"-style classes.  However, it contributes much to both the readability and correctness of these common classes.  Here's the full definition of Person and the test main method in Scala:

That one little line at the top gives you: enforced immutable access to all 5 fields, correct hashCode and equals, and even a decent toString!  This is 1/96th of the code, and with less bugs.  And I'm holding back some additional tricks that Case Classes have.

Of course, Case Classes are not always appropriate.  I've often written hashCodes that did not consider certain fields, etc.  However, many, many classes I've written in Java would have greatly benefitted from a Case Class-type functionality.

I certainly don't spend my days debugging hashCode methods, but Case Classes absolutely address all 3 of my principles for evaluating language differences.


Ben said...

nice post!

Anton said...

even the correct scala hashcode did not save you from the defect.

your scala code will return 100, not 10, man. ;-)

Squirrels Ewer said...

@Anton - how many budding Scala enthusiasts are scarred for life now :) - thanks for finding that.