From the archive: Originally written in January 2011, this post from my old blog, Graham Hacking Scala, has been consistently popular ever since and I thought it deserved a new lease on life here…
I started reading Joshua Bloch’s Effective Java last 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 parameters 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 extend 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 in the language, 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 have a bias towards creating and using immutable classes and immutable fields. 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 in Java 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 (the 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 in Java are not a feature, but merely an artefact of backwards-compatibility. Scala, not trying to be backwards-compatible with Java 1.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?
Scala isn’t without its own foibles and need for best practices. The Engineering team at Twitter have authored an ‘Effective Scala’ page.
Image credits: The Time Has Come! by Kevin O’Mara
Silk Road #9 by Jonathan Kos-Read
A good summary. One way I’ve sold Scala to Java developers is to point out some of these “best Java practices” features. Case classes to reduce boilerplate seem particularly popular.
Item 15: Minimize mutability
One of Josh’s four recommendations for decreasing mutability is to make values final _wherever possible_
It is not true. He recommends to make final only fields of class, not locals or method args. I often see code with final locals and args and that drives me crazy, because doing them final makes no sense, other then noising the code.
I’m wondering of origin of this final practice. It is definitly not from this excellent book.
You’re right that Joshua doesn’t recommend making values final wherever possible. Thanks. I’ve corrected the text.
However, I don’t agree that making parameters and local variables final makes no sense. One good reason for doing this is that it can make it easier to reason about the code while reading it; you never have to think about “what value does this variable have at THIS point in the code?” because no values ever change.
Again, you’re right that using final a lot in Java makes the code a bit noisy. This was part of my point about Scala: the final ‘val’ option is no noisier than the non-final ‘var’ option, and all function params are implicitly ‘val’.
My spouse and i take pleasure in, bring about I came across what precisely I was taking a look intended for. You might have broken our Five day lengthy quest! The lord Thank you guy. Have got a good day time. Ok bye
Pingback: Top 10 Reasons Java Programs Envy Scala - Evolvable Me