「Lombok、便利そうだけどまあそんなに使わないかなあ」と思ってたんだけど試しに使ってみたらかなり快感だったのでメモ。
Javaのだるさ
Javaでプログラムを書いているとよく「intのtype, Stringのnameというフィールドを持つだけの、特定の処理などは含まないHogeクラスを作ろう。まあ普通に文字列表現もあってequalsによる比較とかもできるように」のように思います。
package org.hogel;
public class Hoge {
private int type;
private String name;
public Hoge() {
}
public Hoge(int type, String name) {
this.type = type;
this.name = name;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
return type + (name == null ? 0 : name.hashCode());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj == null || !(obj instanceof Hoge)) {
return false;
}
Hoge other = (Hoge) obj;
if (type != other.getType()) {
return false;
}
return name == other.getName() || name != null && name.equals(other.getName());
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
return builder.append("Hoge(type=").append(type).append(", name=").append(name).append(")").toString();
}
}
うっ、だるい
なんてコードの長さでしょう。ディスプレイのサイズは有限なのです、こんな長いコードを表示するのには何年もかかってしまいます。
そうだ、困ったときのCommonsだ
Javaで困ったときはとりあえずApache Commonsを頼りにしましょう。
Commons Langという便利なライブラリにはこういった独自クラスを作るときに有用な機能が含まれています。
package org.hogel;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
public class Hoge {
private int type;
private String name;
public Hoge() {
}
public Hoge(int type, String name) {
this.type = type;
this.name = name;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
@Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
@Override
public String toString() {
// Hoge[type=1,name=a]のような形式で出力される
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}
hashCode, equals, toStringメソッドの中身がすべて一行になりました。ああしかしやっぱりめんどうです、めんどうなので 一々こんなクラス作るのをやめてListやらMapやらを組み合わせてしまおうか、なんて思ってしまいます。
そうだ、Lombokがある
さてここでLombok。百聞は一見にしかず。Lombokを使うと上述したコードがこんなことになります。
package org.hogel;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Hoge {
private int type;
private String name;
}
おや、ほとんど何もなくなっていますね。でもこれで上に書いた2つのコードとほとんど同じ挙動をするのです。試しにこんなテストコードで実行してみます。
package org.hogel;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;
public class HogeTest {
@Test
public void hogeBean() {
Hoge a1 = new Hoge(1, "a");
Hoge a2 = new Hoge(1, "a");
Hoge b = new Hoge(2, "b");
System.err.println("a1.getType(): "+ a1.getType());
System.err.println("a1.getName(): "+ a1.getName());
System.err.println();
System.err.println("a1: " + a1);
System.err.println("b: " + b);
System.err.println();
System.err.println("a1 == a2: " + (a1 == a2));
System.err.println("a1 == b: " + (a1 == b));
System.err.println();
System.err.println("a1.equals(a2): " + a1.equals(a2));
System.err.println("a1.equals(b): " + a1.equals(b));
System.err.println();
System.err.println("a1.hashCode(): " + a1.hashCode());
System.err.println("a2.hashCode(): " + a2.hashCode());
System.err.println("b.hashCode(): " + b.hashCode());
System.err.println();
assertThat(a1, is(a2));
assertThat(a1, not(b));
assertThat(a1.hashCode(), is(a2.hashCode()));
}
}
定義もしていないコンストラクタやgetType、getNameなどを呼んでいます。それの実行結果がこうなります。
a1.getType(): 1
a1.getName(): a
a1: Hoge(type=1, name=a)
b: Hoge(type=2, name=b)
a1 == a2: false
a1 == b: false
a1.equals(a2): true
a1.equals(b): false
a1.hashCode(): 1089
a2.hashCode(): 1089
b.hashCode(): 1121
なんだか知らんがうまいこと動作していますね。Lombok、すげー。
IDEなしでJavaを書くなんてのはただの苦行ですし、Lombokは各IDEにも対応しています。
詳細や更に便利な機能などはLombokのページでご確認ください。valなんて使いはじめるともうJavaなのかなんなのかわからなくなってきてとても楽しいと思います。