What you'll read in this post:
Get the code directly on github !
Today I would like to share with you my new technical companion: projectlombok.
ProjectLombok like Springfuse is a code generator, and I very much like code generators as they make my life easier. Where springfuse is producing working knowledge spanning many technologies, project lombok produces simple code to bypass java excessive verbosity. Project lombok is generating :
While reading this you should think that eclipse does it for you already since... for ever.
You could also tell me that I should use a real language like scala or a dynamic language like groovy.
Well they could be ideal solutions, but like so many I have no choice but to stick with java.
But there should not be this problem to solve in the first place !
I used to write them manually while using emacs back in the 2000 days, let's skip these painful memories...
We all know how to generate all these methods using eclipse, but I had the curiosity for this post to count the number of steps to generate them:
That’s 11 steps !
Ok, now it’s time to do some eclipse magic and bind "generate setter/getter" to a shortcut.
That’s right, no big deal. But sounds like an ugly solution isn’t it?
And I insist, this problem should not exist in the first place !
So you end up with :
package com.springfuse.blog.projectlombok;
public class UsingEclipse {
private String not;
private String really;
private String fun;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((fun == null) ? 0 : fun.hashCode());
result = prime * result + ((not == null) ? 0 : not.hashCode());
result = prime * result + ((really == null) ? 0 : really.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
UsingEclipse other = (UsingEclipse) obj;
if (fun == null) {
if (other.fun != null)
return false;
} else if (!fun.equals(other.fun))
return false;
if (not == null) {
if (other.not != null)
return false;
} else if (!not.equals(other.not))
return false;
if (really == null) {
if (other.really != null)
return false;
} else if (!really.equals(other.really))
return false;
return true;
}
@Override
public String toString() {
return "UsingEclipse [fun=" + fun + ", not=" + not + ", really=" + really + "]";
}
public String getNot() {
return not;
}
public void setNot(String not) {
this.not = not;
}
public String getReally() {
return really;
}
public void setReally(String really) {
this.really = really;
}
public String getFun() {
return fun;
}
public void setFun(String fun) {
this.fun = fun;
}
}
Basically the usual pojo suspect you see everywhere ...
Commons-lang is giving simple and effective solutions to reduce the boilerplate code with
So you have now only the getter and setter to generate with eclipse and no more need to worry about your equals/hashcode methods.
You should now have this:
package com.springfuse.blog.projectlombok;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
public class UsingCommonsLang {
private String not;
private String really;
private String fun;
public String getNot() {
return not;
}
public void setNot(String not) {
this.not = not;
}
public String getReally() {
return really;
}
public void setReally(String really) {
this.really = really;
}
public String getFun() {
return fun;
}
public void setFun(String fun) {
this.fun = fun;
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
@Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
This is better, less error prone, but we still still have so many getter/setters that we can’t stand.
Now look how we do it using lombok:
package com.springfuse.blog.projectlombok;
import lombok.Data;
@Data
public class UsingProjectLombok {
private String get;
private String fun;
private String back;
}
Isn't it elegant ?
The @Data does express my intention nicely ... and completely as the code is also generated. After the shock of such a concise POJO, the next though you have is for configuration ... Projectlombok gives you this freedom.
package com.springfuse.blog.projectlombok;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@EqualsAndHashCode(exclude = { "get" })
@ToString(exclude = { "get" })
public class UsingProjectLombokWithConfiguration {
protected String get;
@Getter
private String fun;
@Getter
@Setter
private String back;
public UsingProjectLombokWithConfiguration(String get, String fun, String back) {
this.get = get;
this.fun = fun;
this.back = back;
}
}
Please note that Lombok is a good citizen as it gives you clear warnings.
Project lombok is a javac plugin that will use the javac 6 annotation processor. That means that the generation of the methods is part of the compilation, and no runtime nor java agent is required ! It's almost like adding behavior with aspectj but with plain java !
Using lombok and maven is straightforward:
<dependency> <!-- Will be picked by javac -->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>0.8.5</version>
<scope>compile</scope>
</dependency>
...
<repositories>
<repository> <!-- project lombok is not yet in public maven repo -->
<id>projectlombok.org</id>
<url>http://projectlombok.org/mavenrepo</url>
</repository>
</repositories>
...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<compilerVersion>1.6</compilerVersion>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
Execute the lombok.jar and specify your eclipse folder it will add itself or add manually in eclipse.ini the botclasspath and javaagent like this
-javaagent:lombok.jar
-Xbootclasspath/a:lombok.jar
Lombok does work only under javac 1.6, but if you want to target 1.5 just configure it in your pom. But JDK 1.5 has 10 more days before it is no more supported by sun ! It is time to give this info to your boss and make the switch.
The current version 0.8.5 does not work great in this regard, however they fixed it in the trunk. If you do build your own, you'll be fine
git clone git://github.com/rzwitserloot/lombok.git
ant
... or wait for the next binary release !
Edit: the 0.9 release is out
It's in the way
Lombok generates other methods like @Cleanup, @Synchronized, @SneakyThrows or @Logger. I am not sure what to think about these, maybe it could go too far but as the intent is clearly described via the annotation, why not... Anyway I'll stick with the simple features for now and see how it goes.
I have to say, that the combination of google collections and project lombok gave me a fresh view of what you can accomplish with java. It feels like the gap between the language "du jour" and my plain old java has been dramatically reduced. And you know what ?
It feels good !