( ꒪⌓꒪)( ꒪⌓꒪)
( ꒪⌓꒪)( ꒪⌓꒪)
Lombokアノテーションのメモ
Javaのコンパイル時に定型コードを自動生成してくれるLombokをいろいろ触ってみたのでメモする。
@NonNull
メソッドの引数に付与してnullチェックを生成する。
メソッドの先頭でnull
かどうか確認し、nullの場合はNullPointerException
をthrowする。コンストラクタの場合はsuper()
やthis()
の呼び出しの後にチェックされる。
生成前
public void hoge(@NonNull Object fuga){ System.out.println(fuga); }
生成後
public void hoge(@NonNull Object fuga) { if (fuga == null) { throw new NullPointerException("fuga is marked @NonNull but is null"); } else { System.out.println(fuga); } }
@Cleanup
ローカル変数の宣言に付与して、スコープを抜ける前にオブジェクトのクリーンアップ処理を追加する。
具体的にはtry-finally
節の追加とfinally節中にclose()
メソッドの呼び出しを追加する。@Cleanup
の引数にメソッド名を文字列で指定して、close()
メソッド以外を呼ぶこともできる。
生成前
public void hoge() throws IOException { @Cleanup BufferedReader reader = new BufferedReader(new FileReader("testfile.txt")); System.out.println(reader.readLine()); }
生成後
public void hoge() throws IOException { BufferedReader reader = new BufferedReader(new FileReader("testfile.txt")); try { System.out.println(reader.readLine()); } finally { if (Collections.singletonList(reader).get(0) != null) { reader.close(); } } }
@Getter/@Setter
フィールドに付与してgetterとsetterを生成する。メソッド名はフィールド名の先頭を大文字にしてget
/set
をつけたものが生成されるが、フィールドの型がboolean
の場合、getterメソッド名はget
ではなくis
をつけた名前になる。
可視性はデフォルトでpublic
になるが、@Getter
/@Setter
の引数にAccessLevel.*
を指定すると変更することができる。
クラス定義にアノテーションを付与した場合、インスタンスフィールド全てが生成対象になる。 生成したくないフィールドがある場合、AccessLevel.NONE
をフィールドに付与すると生成されなくなる。
生成前
@Setter @Getter private int hoge; @Getter(AccessLevel.PACKAGE) private boolean fuga;
生成後
private int hoge; private boolean fuga; public void setHoge(int hoge) { this.hoge = hoge; } public int getHoge() { return this.hoge; } boolean isFuga() { return this.fuga; }
@ToString
クラス定義に付与して、toString()
メソッドの実装を生成する。 デフォルトでは、クラス名(フィールド名1=値, フィールド名2=値,...)
形式の文字列を返す。アノテーションの引数にincludeFieldNames=false
を追加するとフィールド名を取り除くことができ、callSuper=true
を指定すると親クラスのtoString()
メソッドの結果を含めることができる。
デフォルトではインスタンスフィールド全てが文字列の生成対象となる。除外したい場合はフィールドに@ToString\.Exclude
アノテーションを付与する。もしくはクラス定義の@ToString
にonlyExplicitlyIncluded=true
を指定して表示したいフィールドのみ@ToString\.Include
を付与することで、表示するフィールドを制御できる。
引数なしのインスタンスメソッドに@ToString\.Include
を付与することで、メソッドの実行結果も生成文字列に含めることもできる。
生成前
@ToString(callSuper=true) public class Sandbox { private int hoge; private boolean fuga; }
生成後
public String toString() { return "Sandbox(super=" + super.toString() + ", hoge=" + this.hoge + ", fuga=" + this.fuga + ")"; }
@EqualsAndHashCode
equals()
とhashCode()
メソッドを生成する。
生成前
@EqualsAndHashCode public class Sandbox { @NonNull private Integer hoge; private boolean fuga; }
生成後
長いので省略。equals()
はフィールド同士をequals()
メソッドで比較する。
コンストラクタ生成(@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor)
@NoArgsConstructor
引数なしのコンストラクタを生成する。
未初期化のfinalフィールドを持つ場合コンパイル時エラーとなるが、force=true
を指定した場合は0かfalseかnullで初期化される。@NonNull
を付与したフィールドがあった場合でもnullチェック処理は生成されないので注意が必要。
生成前
@NoArgsConstructor() public class Sandbox { @NonNull private Integer hoge; private boolean fuga; }
生成後
public class Sandbox { @NonNull private Integer hoge; private boolean fuga; public Sandbox() { } }
@RequiredArgsConstructor
すべての初期化されていないfinalフィールド・@NonNull
が付与されたフィールドを引数に持つコンストラクタを生成する。引数の順序はクラス内の出現順と同じ。
@NonNull
のnullチェックは生成される。
生成前
@RequiredArgsConstructor() public class Sandbox { @NonNull private Integer hoge; private boolean fuga; }
生成後
public class Sandbox { @NonNull private Integer hoge; private boolean fuga; public Sandbox(@NonNull Integer hoge) { if (hoge == null) { throw new NullPointerException("hoge is marked @NonNull but is null"); } else { this.hoge = hoge; } } }
@AllArgsConstructor
全てのフィールドを引数に持つコンストラクタを生成する。
@NonNull
のnullチェックは生成される。
生成前
@AllArgsConstructor() public class Sandbox { @NonNull private Integer hoge; private boolean fuga; }
生成後
public class Sandbox { @NonNull private Integer hoge; private boolean fuga; public Sandbox(@NonNull Integer hoge, boolean fuga) { if (hoge == null) { throw new NullPointerException("hoge is marked @NonNull but is null"); } else { this.hoge = hoge; this.fuga = fuga; } } }
@Data
@ToString
, @EqualsAndHashCode
, @Getter
, @Setter
, @RequiredArgsConstructor
を指定した場合と同じ。
生成前
@Data public class Sandbox { @NonNull private Integer hoge; private boolean fuga; }
生成後
長いので省略。生成されたメソッドは次の通り。
@RequiredArgsConstructor
public Sandbox(@NonNull Integer hoge)
@Getter
/@Setter
public Integer getHoge()
public boolean isFuga()
public void setHoge(@NonNull Integer hoge)
public void setFuga(boolean fuga)
@EqualsAndHashCode
public boolean equals(Object o)
protected boolean canEqual(Object other)
public int hashCode()
@ToString
public String toString()
@Value
@Dataをimmutableにしたもの。すべてのフィールドはprivate finalに設定され、Setterは生成されない。
クラスにもfinalが設定されるが@NonFinal
を指定していると設定されない。 生成されるメソッドが先に定義されていた場合は、メソッドが生成されないだけでエラーは発生しない。
生成前
@Value public class Sandbox { @NonNull private Integer hoge; private boolean fuga; }
生成後
こちらも省略。
@Builder
クラス定義に付与してBuilderクラスを生成する。生成するコードは次の通り。
Innerクラスの
${クラス名}Builder
を生成するBuilderインスタンスを返す
builder()
メソッドを生成する。${クラス名}Builder
に次のメソッド・フィールドを生成する
生成前
@Builder public class Sandbox { @NonNull private Integer hoge; private boolean fuga; }
生成後
public class Sandbox { @NonNull private Integer hoge; private boolean fuga; Sandbox(@NonNull Integer hoge, boolean fuga) { if (hoge == null) { throw new NullPointerException("hoge is marked @NonNull but is null"); } else { this.hoge = hoge; this.fuga = fuga; } } public static Sandbox.SandboxBuilder builder() { return new Sandbox.SandboxBuilder(); } public static class SandboxBuilder { private Integer hoge; private boolean fuga; SandboxBuilder() { } public Sandbox.SandboxBuilder hoge(Integer hoge) { this.hoge = hoge; return this; } public Sandbox.SandboxBuilder fuga(boolean fuga) { this.fuga = fuga; return this; } public Sandbox build() { return new Sandbox(this.hoge, this.fuga); } public String toString() { return "Sandbox.SandboxBuilder(hoge=" + this.hoge + ", fuga=" + this.fuga + ")"; } } }
@Synchronized
メソッドに付与して、指定したオブジェクトでロックするsynchronized
ブロックを追加する。
デフォルトの場合、インスタンスメソッドは$lock
フィールド、クラスメソッドは$Lock
クラスフィールドが使用される。 存在しない場合はprivate
で作成される。
別のフィールドでロックしたい場合は@Synchronized
の引数にフィールド名を文字列で指定する。
生成前
@Synchronized public void hoge(){ System.out.println("hoge"); } @Synchronized public static void fuga(){ System.out.println("fuga"); }
生成後
private final Object $lock = new Object[0]; private static final Object $LOCK = new Object[0]; public void hoge() { synchronized(this.$lock) { System.out.println("hoge"); } } public static void fuga() { synchronized($LOCK) { System.out.println("fuga"); } }
@Log
クラス定義に付与すると、static finalなlog
フィールドを生成し指定したロガーで初期化する。各ロガー実装に応じたアノテーションが用意されている。
生成前
@Log public class Sandbox { @NonNull private Integer hoge; private boolean fuga; }
生成後
private static final Logger log = Logger.getLogger(Sandbox.class.getName());
参考
はてなブログおみくじ2014
はてなブログおみくじ2014
あけましておめでとうございます。
~/.ssh/known_hostsから指定したホストの鍵を取り除く
知らなかったのでメモ。
$ ssh-keygen -R (hostname)
3月が延長されたと聞いて
3月を延長するのをやめました(復旧済み) - はてなブログ開発ブログ
記事のURLは2013/04/01になっていて悲しい。
rbenv, ruby-build導入メモ
複数のバーションのrubyを切り替えられるようにするrbenvとruby-buildをインストールしてみたのでメモします。
まずは、homebrewでコマンド本体をインストールします。
% brew install rbenv ruby-build
インストール途中にシェルの設定が書かれているのでこの通り追加します。PATHの設定、補完関数の追加、rbenvコマンドの再定義などをしているようです。
2行目を~/.zshrcに追加すればいいのですが、zshの場合は$(rbenv init -)を$(rbenv init - zsh)に変えないとエラーが出るので注意です。
To enable shims and autocompletion add to your profile: if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi
設定を読み直してエラーを吐かなければOKです。
% source .zshrc
次にRubyのビルドのための準備です。OSX標準のopensslとreadlineはアレらしいのでhomebrewからインストールします。
% brew install openssl % brew link openssl % brew install readline % brew link readline
installサブコマンドを-l付きで実行するとインストール可能なrubyのバージョンが表示されるので、ここから選択します。JRubyも入っているんですね。
% rbenv install -l Available versions: (以下長いので適当に抽出) 1.8.6-p420 1.8.7-p249 1.8.7-p302 1.8.7-p370 1.9.3-p286 jruby-1.7.0 maglev-1.0.0 rbx-1.2.4 rbx-2.0.0-dev ree-1.8.7-2012.02
先ほどインストールしたopensslとreadlineにリンクさせる為にオプションを付けてインストールコマンドを叩きます。
% CONFIGURE_OPTS="--with-readline-dir=$(brew --prefix readline) --with-openssl-dir=$(brew --prefix openssl)" rbenv install 1.9.3-p286
rbenvのrubyの切り替え方法は以下の3つがあります。
- global そのユーザで有効
- local そのディレクトリで有効
- shell そのシェルで有効
今回は普段使うバージョンの選択なのでglobalサブコマンドでバージョンを指定します。
% rbenv global 1.9.3-p286 % ruby --version ruby 1.9.3p286 (2012-10-12 revision 37165) [x86_64-darwin12.2.1]
1.9.3p286を使うことができています。
仕組みはこちらで解説されていますが、~/.rbenv/versions以下に各バージョンのバイナリをインストールし、~/.rbenv/shims/に使用するバージョンのシンボリックリンクをおいてPATHを通しているようです。
またgemsetは上の3つの切り替え方法とは関係なく、現在選択しているrubyのバージョンに紐付いているようです。gemで実行コマンドをインストールするような操作をした場合、rbenv側で対応するシンボリックリンクを作成しなければならないため、コマンド実行前に
% rbenv rehash
を実行する一手間が必要になります。