Monday, January 10, 2011

Scala == Effective Java ?

I started reading Joshua Bloch's Effective Javalast week. I'll have to admit that I haven't read it before, but only because I've been told by several people, "you already do most of what's in there anyway." Seeing as we tell all the new recruits to read it, I thought I should actually flip through it myself so I know what's in there.

Books of best practices are always written in relation to domains that have many possibilities for bad practices (choosing otherwise would make for a very short book). Reading the first chapter of Effective Java, I was amused as I realised that, if you're coding in Scala instead of Java, many of the book's recommendations are either unnecessary, because Scala doesn't permit the corollary bad practice, or built into the language of Scala, or made easier to implement than they are in Java. This isn't a criticism of the book, but an observation that the state of the art is moving on, and Java is being left behind.

From the first 25 items in the book, here are my notes on practices that either become easier to follow or unnecessary if you are using Scala:

Item 1: Consider static factory methods
One of the four advantages given for static factory methods is that construction of classes with type parameters is briefer through static methods because the type parameters can be inferred on a parameterised method call. Scala solves this one by inferring type parameters on constructors, but not only that. It infers LOTS of types, almost everywhere: fields, local variables, method return types, anonymous function parameters - all can usually have their type inferred from the context, meaning that Scala code has a lot less declarative noise, while still remaining statically type-checked.

Item 2: Consider the Builder pattern instead of long constructors
Joshua writes himself that the Builder pattern simulates the combination of named parameters and default parameters that are found in other languages, and Scala supports named and default parameters. What he means by "simulates" is that you need to write a whole extra Builder class in Java to get the nice Builder effect at the client site, whereas in Scala, you essentially get a Builder for free every time you write a constructor.

Item 3: Enforce Singletons
While code-enforced singletons have gone out of fashion a bit with the popularity of IoC containers, they're still around a lot and these rules are still important. Scala supports singletons at the language level, providing the 'object' keyword which, substituted for the word 'class' in a declaration, will compile a singleton object into your code instead of a class. The generated object follows all of Josh's recommendations, including deserializing to the same instance (as long as you use Scala's @serializable rather than extends java.io.Serializable)

Item 4: Enforce non-instantiability with private constructors
There's really only two cases where you want to do this: singletons (see above), and utility classes, which are really just singletons that have no state. Either way, Scala's 'object' keyword for creating singletons is the simple answer.

Item 5: Avoid auto-boxing
The recommended panacea for unwanted auto-boxing is to prefer primitives wherever possible. Now, Scala is a purely object-oriented language, which means there are no primitives, and all numbers, characters and booleans are represented at the language level as objects. However, Scala uses of these primitive wrapper objects compile to byte code which uses Java's primitives wherever possible, so this recommendation is implemented for you by the Scala compiler.

Items 7, 8 and 9: Overriding toString, hashCode and equals
If you're authoring an immutable data structure, declaring your Scala class as a case class will tell the compiler to automatically implement toString, hashCode and equals for you, along with an unapply() method that can be used in a pattern matching clause. (There are some disadvantages to using Scala's case classes, but I believe they work well for most situations.)

Item 11: Override clone() judicously
While Scala as a language doesn't provide an answer to this one, it's considered best-practice Scala to favour immutable types, with transformation being much favoured over mutation. Following this principle will reduce the need to ever use clone(), because immutable objects can be shared among many clients and threads without the shared-mutable-state worry that might cause you to consider cloning something.

Item 14: Use public accessors methods rather than public fields
While Scala appears to have public fields - and indeed to make fields public by default - in fact Scala implements Bertrand Myer's "Uniform Access Principle", with all access to fields (which are in fact all private) being made through accessor and mutator functions that have the same name as the field. In other words, the compiler writes get and set methods for you and everything that looks like field access in your code actually goes through these methods.

Item 15: Minimize mutability
As already mentioned, it's considered Scala best practice to shun mutable state as much as possible. One of Josh's four recommendations for decreasing mutability is to make values final wherever possible. All Scala fields and local variables must be preceded by either 'val', indicating immutability (of the reference), or 'var', indicating that the reference can change. (Function parameters are always vals, and hence final.) Forcing programmers to make this choice when each variable is declared encourages the practice of using a lot of immutability, compared to final which is an optional modifier and seen by many as extra noise in most declarations.

Item 18: Prefer interfaces to abstract classes
Scala has traits - abstract classes that essentially allow multiple inheritance of function definitions (as opposed to interfaces, which only inherit function declarations). The possibility of multiple inheritance discounts quite a few of the disadvantages Josh raises against using abstract classes in Java. Of course, it also introduces some new issues, and exacerbates some others, like those Josh lists in Item 16 about preferring composition+delegation over inheritance. Multiple inheritance is a double-edged sword, for sure.

Item 21: Use function pointers
Scala, as a functional language, has functions as first-class members of the language, with every function naturally being available as an object (same as what Josh calls a function pointer) should the need arise. It also supports anonymous, inline functions, which, if available in Java, could reduce current "function pointer" logic like this:
new Thread(new Runnable() { public void run() { System.println("Running"); } } );
down to something like this:
new Thread({System.println("Running")})

Item 23: Don't use raw generic types
Scala doesn't allow you to write code that uses raw generic types, even if those types are defined in Java: it just won't compile. For what it's worth, raw generic types are not a feature, but merely an artefact of backwards-compatibility. Scala, not trying to be backwards-compatible with Java 4, just doesn't need raw types and, as a result, is able to provide stricter type safety for type-parameterised classes and functions.

Item 25: Prefer Lists over Arrays
While you should probably still prefer Scala's List class to Arrays for most applications, Scala prevents the chief problem cited with Java's arrays. In Java, you can cast a String[] to an Object[] and then assign a Long to one of the entries. This compiles without error, but will fail at runtime. In Scala, however, arrays are represented in source code as a parameterized class, Array[T], where T is an invariant type parameter. This basically just means that you can't assign an Array[String] to an Array[Object], and so Scala prevents this problem at compile time rather than choking at runtime.

Scala == Effective Java ?
So I'm going to put this question out there:
If 'Effective Java' is considered essential reading, and the best practices in it are the de facto standard for writing good programs, shouldn't we all be giving serious consideration to switching to a language that is so very close to Java, but makes good programming even easier?

Want to learn more?
From Amazon...
From Book Depository...

14 comments:

  1. Even more, if you follow the discussion about equals (comparing point with color point, IIRC) and think about the comparable interface, the most elegant solution for that is implicit conversions/type classes

    ReplyDelete
  2. Great article! I had a similar feeling when I read "Java Puzzlers" and saw that most problems were already solved in Scala.

    ReplyDelete
  3. Much of what you mention rings true with me as I came to Java from ActionScript. Really like your analysis

    ReplyDelete
  4. I came to the exact same conclusions reading Effective Java. That made me want to program in Scala even more ! I don't understand why Joshua Bloch isn't backing Scala, maybe he sees it as a contender ?

    ReplyDelete
  5. PS: I wrote about it here
    http://joeamined.wordpress.com/2010/07/28/joshua-bloch-chess-and-scala/
    (See the "Scala language" section).

    ReplyDelete
  6. Do you know the new (Scala 2.8) copy method for case classes? It's a clone on steroids, hence item 11 is also well served by Scala.

    ReplyDelete
  7. My experience is that the IDE support is still lagging behind. I've tried Eclipse and IDEA, and both didn't really work as well as they do with Java. Slow, debugging and refactoring failing, syntax highligting/indentation weirdness ...

    What's your experience with IDEs and Scala?

    ReplyDelete
  8. Hi Mikkel,

    IDE support for Scala is definitely not as sophisticated as it is for Java, but I don't think we should expect that yet. Tools built for Java have now had about 16 years to advance, whereas the Scala plugin for IDEA was started just four years ago. Having said that, even what we have now for Scala is far, far more advanced than what JBuilder was offering for Java back in 2000 when it was four years out of the blocks.

    It sounds like you've found IDE support deficient to the point of being unusable. I know I found it quite clunky when I started coding Scala a bit over a year ago, but I've seen the IDEA Scala Plugin improve dramatically over that time. While it still doesn't do a lot of things for me, it's very workable. I can write code, compile it, run it, run tests with Scala-based test frameworks. (If you're finding that compiling Scala is slow, you might want to try turning on the fast scala compiler (fsc).)

    We also have to consider that people are doing serious things with Scala: Twitter, LinkedIn and FourSquare are all making use of it in their production systems. If large startups are writing successful production systems in Scala, that surely says the the IDE support is not stopping people from producing good code. It's not at the point of writing the code for you yet, but it's good enough, and getting better all the time.

    Cheers,

    Graham.

    ReplyDelete
  9. Great blog...
    I am Java developer from last 7 year... and going through Scala as of now.. But if you see industry ...there are not much development work in Scala and trend show there will not in near future.

    ReplyDelete
  10. Jesus, my eyes just totally glaze over whenever I read anything about Java, Scala, etc. How does anyone program ANYTHING in crap like this? I'd rather die. You guys have my sympathies.

    ReplyDelete
  11. That's not to say that Scala doesn't deserve it's own "Effective Scala" book, but that will probably cover things like how work with FP, immutability, and Scala's type system effectively.

    ReplyDelete
  12. good thought buddy , Scala is indeed great language but i don't think it can ever compete java.

    Javin
    How volatile keyword works in Java with example

    ReplyDelete