A common refrain of Developers is just how verbose the language is, especially compared to its more dynamic counterparts like Scala or Ruby. While Java cannot be made as concise as a true dynamic language, it is still possible to make the code more readable and provide clarity to the business logic.
What Makes a Language More Concise
Verbose code is possible in any language. The language’s structure, standard library and the common idioms are the limiting factors in how concise a developer can make the code. Brevity is as important as clarity.
These are some tricks that I’ve learned to help make Java less Java-like.
Explicit vs Convention
In my opinion, this is the easiest way to make code that is both clean and understandable. Frameworks like Ruby on Rails reduce the configuration that maps components together, opting instead to define the relationships through standard conventions. Models go in one place and extend a common class, the same with controllers. There is meta programming and magic everywhere, but the consistency of how it’s applied keep the system maintainable.
Java does not have all the meta programming capabilities of a fully object-oriented and functional language like Ruby, Python, or even Javascript, but there are some mechanisms available to remove unnecessary boilerplate.
AOP
Aspect Oriented Programming is Java’s mechanism for dealing with cross-cutting concerns. Logging, transactions, and security are all examples of these types of concerns.
The following is relatively common Java code (all code in this post is for illustration, it probably doesn’t run):
public Post saveBlogPost(User author, String text) {
log.info("Entering Save Blog Post");
if(permissionsService.hasPermission(author, "saveBlogPost")) {
Session s = sessionFactory.getSession();
Transaction tx = null;
try {
tx = s.beginTransaction();
Post post = new Post()
.setAuthor(author)
.setText(text);
postDao.save(post);
tx.commit()
log.info("Successfully saved post");
return post;
} catch(Exception e) {
log.error("An exception occurred: " + e.getMessage(), e);
if(null != tx && tx.isActive()) {
tx.rollback();
}
}
}
}
The business logic is very simple, but there is a lot of boilerplate. By using AOP, the method could be reduced to:
public Post saveBlogPost(User author, String text) {
Session s = sessionFactory.getSession();
Post post = new Post()
.setAuthor(author)
.setText(text);
postDao.save(post);
return post;
}
With all the boilerplate removed, it is much easier to understand what the business logic of this segment of code is. AOP allows us to accomplish this by adding advice
around joinpoints
, which in this case is the method saveBlogPost
. I won’t go into details of how to implement AOP here and instead point you to a primer.
Annotations not XML
Annotations can be coupled with AOP to make code much simpler. Spring MVC has a great library of annotations that make creating a web app much simpler.
Annotations can also replace XML for more general configuration. An example of this is Hibernate. From the Hibernate doc, the traditional, way to map a class would be the following:
<class name="Employee" table="EMPLOYEE">
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="firstName" column="first_name" type="string"/>
<property name="lastName" column="last_name" type="string"/>
<property name="salary" column="salary" type="int"/>
</class>
Annotations simplify this by allowing the developer to write:
@Entity
@Table(name = "Employee")
class Employee {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String firstName;
private String lastName;
private int salary;
}
By declaring the mapping properties with the properties themselves, the developer can cut down on an additional file containing essentially the same information as the POJO.
Static Import
Sometimes you want to call common functionality in a situation where an abstract
or parent class does not make much sense.
Mix-ins are a popular approach for code libraries for common code that does not match an inheritance library. The JAVA version is are the more cumbersome static
common classes. Not one can one not share state but the syntax requires explicit call outs to other classes.
At least the latter can be fixed with static imports. Therefore, it is possible to turn:
Assert.assertEquals(...)
into
import static org.junit.Assert.*
...
assertEquals(...)
I find this relatively minor piece of syntactic sugar helps a lot with code clarity, especially if care is taking to name the methods correctly.
Conclusions
Anytime one makes a style decision that take the approach way from the most idiomatic form, there is a potential to create something that is difficult to maintain. The desire to make code more readable by removing boilerplate definitely comes with trade-offs. If other developers are not familiar with the patters or, as is the case with AOP, the patterns themselves are difficult to debug, the cost to support the software may increase.
However, I find that making code simpler also makes it easier to develop and more approachable for the most common cases of maintenance. These are not the only approaches and the newer versions of Java will have even more support for advanced language concepts (closure support for one). Despite the reputation, simplicity is possible and writing Java can feel as lightweight as using more trendy, dynamic languages.
Did you like this content?
Related Posts:
The (Almost) Universality of the $onInit Lifecycle Callback in AngularJS
AngularJS 1.x's Component functionality brought with it new lifecycle callbacks that are not restricted to controllers within Components.
Yahoo News Style Floating Right Panel
Javascript for floating a shorter column next to a longer one
Creating Rich Lists with lists.js
ListJs offers a clean interface and simple API for managing lists in html