zondag 11 september 2016

What's wrong with Java Beans?

TLDR: This is my rant against the concept of Java Beans.
Im my previous post I mentioned that beans are a great way to compose a solution out of smaller parts. I also mentioned writing generators for the boilerplate of a bean. Such generators are now standard in modern IDEs.  Sadly one important thing Java does not have is real properties. Syntactic sugar for properties is present in  languages like: C#,  F#, Scala, Kotlin and Ceylon.

Programming is a way of communicating your ideas:
  1. To yourself
  2. To your later self
  3. To your coworkers
  4. To the unknown people that have to maintain your crap(often politely called legacy).
  5. To outside programmers, aka customers, if you are writing a library.
In daily life we use  words, sentences paragraphs etc to express our ideas. For a frequently used idea we use  single word as  succint way of communication, a shared understanding.

Java Beans are a little bit more verbose. They use sentences to express  simple ideas:
  • For just a simple object holding a Person's details you have to add fields, getters with comments and setters with comments. 
  • Supporting one field requires at least 15 lines of code.
  • These lines need tests too! 
  • This code needs to be maintained by you, your future self, coworkers, and the guy after you.  Customers have to read about the getters and the setters from the Javadocs.
  • These lines clutter your codebase.
When Microsoft copied  improved upon Java by inventing C# they did at least one thing right. They introduced syntactic sugar for properties. Nowadays a simple property in C# is just one line, almost like a public field.  Example:

public string Name{get; set;}="John Doe";

That is:
  • 14 lines less to be read. No clutter!
  • One line to be maintained by you.
  • One line to be maintained by your future self.
  • One line to be maintained by coworkers.
  • One line to be maintained by the guy after you.
  • Much less documentation to be written.
  • Less tests to write.
  • More time to break other stuff.
Languages such as F#, Scala, Kotlin and Ceylon even improved this further. A simple class definition in these languages is a one liner:

class Person(string name, string address);

Often forgotten methods

Write default constructor, the getters, setters, the related tests and the Javadoc and your beautiful bean  is ready for use? Wrong!. When your bean is used in  collections the collection framework uses equals(...) and hashCode() for sorting bean instances. If you do nothing the equals() and hashCode()  of the super class  Object  will be used. This is usually not what you want.

For debugging purposes having a custom toString() function is a nice to have,
If you add these you already  have some code to maintain and write tests for.

Unlike C#, Java does not allow operator overloading so there are no operators to overload.

 F#,  probably other languages too, even goes a bit further by also including sensible implementations for comperator functions. These functions are more important if you use Streams. To the .NET programmer: Streams are Java's way to LINQ without syntactic sugar in the language.

Ease the pain with code generation

IDEs like Eclipse come with  code generation functionality. You can:
  • generate getters and setters form fields.
  • generate a  hashCode() and equals(...) function.
  • generate a toString() function.
 In a language like F# these functions are generated by the compiler automatically. developers do not see these functions so nothing to maintain or break.

Sadly there is no support for maintenance. Maintenance when adding, changing or deleting fields to a bean, are left to the craftsmanship of the developer. This will not change in coming Java version 9.  We have to live with it for near future.

2018-02 Update: Succinctly testing and writing Java Beans

Originally I moaned about testing whether a Bean is a Bean and that it is top Java verbosity.
The last 1.5 years I have bean using Bean Matchers and Lombok to fix those issues.

Bean Matchers 

If you want to test whether the bean you created and update is a proper bean there is the handy Bean Matcher library, that can be used in unit tests. It allow you to check for  proper getters, setters, default constructor, hashCode() and equals().  Bean Matchers build on top of Hamcrest Matchers and can be used within Junit tests. You still may need to write a custom matcher that utilizes some reflection to check all classes in "that domain model package".


If you grew tired of writing "Get me, set me I am a bean"  you can use the Lombok toolkit and library
that allows you to define a class with just fields and some special annotations to get standard bean behavior.

Disadvantage, you have to do some Maven trickery to get proper documentation generated.

Lombok comes with  a plugin for Eclipse and other IDEs to ensure that it does not scream about missing getters/setters and also make "Intellisense" for generated code work.

Other goodies it as are:

  •  A @UtilityClass generates a private constructor that throws a NotSupportedException  for classes with only static methods. Only problem is, how to teach checkstyle not to scream about classes missing a default constructor that are decorated with @Utilityclass.
  •  Local variable type inference using a  var "keyword".  This may come to Java 10 according to JEP 286.  Basically this means you do not have to write a type twice when doing an assignment, eg MyType x = new MyType(); becomes var x = new MyType();  This may be especially useful when using java.util.streams in combination with  deeply nested generics, e.g. the case where you do not care about Stream<Foo<Buzz<Bar>>>. Local Type inference is already present in in C# 3.0 since 2007. It might come to Java in 2018... In .NET it is used often when using LINQ, a language extension equivalent to Java's java.util.streams but less verbose.