第三回ありえる社内勉強会 「いわががのLombok」
Upcoming SlideShare
Loading in...5
×
 

第三回ありえる社内勉強会 「いわががのLombok」

on

  • 7,681 views

 

Statistics

Views

Total Views
7,681
Views on SlideShare
5,873
Embed Views
1,808

Actions

Likes
15
Downloads
21
Comments
0

11 Embeds 1,808

http://kiris.hatenablog.com 946
http://cs.hatenablog.jp 504
http://d.hatena.ne.jp 261
https://twitter.com 51
http://a0.twimg.com 21
http://www.feedspot.com 8
http://hatenatunnel.appspot.com 6
http://cloud.feedly.com 5
http://webcache.googleusercontent.com 4
http://b.hatena.ne.jp 1
http://www.google.co.jp 1
More...

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

第三回ありえる社内勉強会 「いわががのLombok」 第三回ありえる社内勉強会 「いわががのLombok」 Presentation Transcript

  • 第3回 ありえる社内勉強会 「いわががのLombok」
  • お前だれよ? twitter: @kiris いわなが?いわがが?
  • Lombokって何? http://projectlombok.org  created by Javaの冗長性を排除する為  Roel Spilker のライブラリ 「赤唐辛子」の意味 v0.10.4 MIT license  Reinier Zwitserloot
  • Javaの冗長性って? こういうのとか class Data { private int value; public int getValue() { return value; } public void setValue(int value) { this.value = value; } }
  • Javaの冗長性って? 後、こういうのとか… InputStream in = new InputStream(args[0]); try { ... } finally { If (in != null) in.close(); }
  • Javaの冗長性って? 他にも、こういうのとか… Map<String, List<String>> map = new HashMap<String, List<String>>(); ... for(Map.Entry<String, List<String>> entry : map) { ... }
  • Javaの冗長性って? …… class MyClass { private static Log log = LogFactory.getLog(MyClass.class); private final String name; public MyClass(String name) { if (name == null) { throw new NullPointerException(); } this.name = name; } @Override public int toString() { return “MyClass(name=”+ this.name +“)”; }
  • Javaの冗長性って?  こうならない為のLombok! 続きはWebで!!
  • 冗長の何がいけないの? 生産性が下がる コード量が増えて読みづらくなる バグが入り込む可能性がある 死にたくなる
  • Lombokの導入
  • Lombokを入手する Download lombok.jar  http://projectlombok.org/download.html Maven or Ivy  http://projectlombok.org/mavenrepo/index.html
  • Lombokを使う Javac  Classpathに追加 GWT  java -javaagent:lombok.jar=ECJ Play Framework  https://github.com/aaronfreeman/play-lombok#readme ECJ  java -javaagent:lombok.jar=ECJ -Xbootclasspath/p:lombok.jar -jar ecj.jar -cp lombok.jar
  • LombokをIDEでも使う Eclipse, NetBeans なんかに対応  IDEA IntelliJはまだ未対応 java -jar lombok.jar
  • Lombokを試してみる
  • @Data import lombok.Data public @Data class DataExample { private final String name; private int count; private List<Object> list; } @Dataの主な機能  全てのフィールドのgetter / setter の生成  toString, equals, hashCodeの生成  finalフィールドを引数にしたコンストラクタの生成
  • 結果の確認(delombok) 変換後のコードを出力  java -jar lombok.jar delombok -p ${src} ファイルとして保存  java -jar lombok.jar delombok -d ${output} ${src} Ant  <delombok verbose="true" encoding="UTF-8" to="$ {output}" from="${src}" /> Maven  https://github.com/awhitford/lombok.maven
  • @Data(変換後)public class DataExample { private final String name; private int count; private List<Object> list; public DataExample(String name) { this.name = name; } public String getName() { return name; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } @Override public String toString() { ... } @Override public boolean equals(Object other) { ... } @Override public int hashCode() { … }}
  • Eclipseからも即時反映 その場でアウトラインや補完候補に表示されます
  • 他の機能 @Getter / @Setter @Getter(lazy=true) @ToString @EqualsAndHashCode @NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor @Data @Cleanup @Synchronized @SneakyThrows @Log val @Delegate http://projectlombok.org/features/index.html
  • @Getter / @Setter Getter / Setterの自動生成 @Dataよりも優先 public class GetterSetterExample { @Getter @Setter private String name; @Getter(AccessLevel.PROTECTED) private int age; }
  • @Getter / @Setter(変換後) Getter / Setterの自動生成 @Dataよりも優先 public class GetterSetterExample { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } protected int getAge() { return age; } }
  • @Getter(lazy=true) いわゆるメモ化 サブルーチン(関数)呼び出しの結果を保持し、再利用するこ とで、そのサブルーチンの呼び出し毎の再計算を防ぐ public class GetterLazyExample { @Getter(lazy=true) private final double[] cached = expensive(); private double[] expensive() { ... } }
  • @Getter(lazy=true)(変換後) いわゆるメモ化 サブルーチン(関数)呼び出しの結果を保持し、再利用するこ とで、そのサブルーチンの呼び出し毎の再計算を防ぐ public class GetterLazyExample { public double[] getCached() { // 本当はthread-safe if (!this.$lombok$lazy1i) { this.$lombok$lazy1v = expensive(); this.$lombok$lazy1i = true; } return this.$lombok$lazy1v; } private double[] expensive() { ... } }
  • @Cleanup リソースの片付けを自動で行なう public static void main(String[] args) throws IOException { @Cleanup InputStream in = new FileInputStream(args[0]); @Cleanup("release") MyResource resource = new MyResource(); ... }
  • @Cleanup(変換後) リソースの片付けを自動で行なう public static void main(String[] args) throws IOException { InputStream in = new FileInputStream(args[0]); try { MyResource resource = new MyResource(); try { ... } finally { if (resource != null) resource.release(); } } finally { if (in != null) in.close(); } }
  • @Synchronized this以外のロックオブジェクトで排他 public class SynchronizedExample { private final Object readLock = new Object(); @Synchronized private int foo() { return 1; } @Syncrhonized("readLock") private int bar() { return 2; } }
  • @Synchronized(変換後) this以外のロックオブジェクトで排他 public class SynchronizedExample { private final Object $lock = new Object(); private final Object readLock = new Object(); private int foo() { synchronized($lock) { return 1; } } private int bar() { synchronized(readLock) { return 2; } } }
  • val ローカル変数の型宣言を省略 public static void main(String[] args) { val map = new HashMap<String, List<String>>(); ... for(val entry : map.entrySet()) { ... } }
  • val(変換後) ローカル変数の型宣言を省略 public static void main(String[] args) { final Map<String, List<String>> map = new HashMap<String, List<String>>(); ... for(final Map.Entry<String, List<String>> entry : map.entrySet()) { ... } }
  • etc @ToString  toStringの生成 @EqualsAndHashCode  equalsとhachCodeの生成 @NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor  コンストラクタの生成 @SneakyThrows  チェック例外を非チェック例外のようにthrowする @Delegate  移譲処理の生成 http://projectlombok.org/features/index.html
  • Lombokのメリット コードの冗長性の排除  生産性が上がる  コードの見通しを良くなる  バグを埋め込む可能性を減らす  心の平穏
  • Lombokのデメリット 魔法に見える(not WYSIWYG) Lombokのバグに悩まされる可能性がある リファクタリング機能との衝突 デバッグがややこしくなる
  • ここまでのまとめ LombokはJavaの冗長性を排除する Lombokの導入はとても簡単 魔法には代償をともなう
  • Break time
  • Lombokの仕組み
  • ソースコード生成?バイトコード生成? いいえ、AST変換です JavaのASTを直接生成・変換してます  ソースコード生成と違ってコードが膨れあがりません  バイトコード生成と違って同じコンパイル単位のクラスか らも可視的です
  • Lombokが保持するAST JavacとECJの二つのASTを別々に保持 AnnotationHandlerも各AST毎に実装する必要がある 二つのASTを統合するためのプロジェクトも進行中  https://github.com/rzwitserloot/lombok.ast
  • Lombokの処理の流れ
  • エントリーポイント lombok.javac.apt.Processor  implements javax.annotation.processing.Processor lombok.eclipse.TransformEclipseAST  EclipseのParserにパッチを当てて実行  https://github.com/rzwitserloot/lombok.patcher  OSGi ClassLoaderに注入されて実行される
  • AnnotationHandlerの読み込み プラグイン形式の読み込み @ProviderFor(JavacAnnotationHandler.class)  used SPI(http://code.google.com/p/spi/)  Service Provider Interfaceのwrapper
  • ASTの探索 ASTをトラバースしてアノテーションを探索 アノテーションが見付かったら、 対応するAnnotationHandlerのhandleを実行する AnnotationVisitor  Implements JavacASTVisitor  独自のVisitorも定義可能  @ProviderFor(JavacASTVisitor.class)  HandleVal
  • ASTの変換 各AnnotationHandlerや各ASTVisitorで 変換にはJavacなどの非公開APIを直接使用  com.sun.tools.javac.tree  org.eclipse.jdt.internal
  • Lombokを拡張する
  • Lombokを拡張するには? Lombokは外からの拡張を意識して作っているわけではない Lombok本体を模範することで拡張することは出来る
  • @Perf メソッドの実行時間を出力 public class PerfExample { @Perf void foo() { ... } }
  • @Perf(変換後) メソッドの実行時間を出力 public class PerfExample { void foo() { long $start = System.nanoTime(); try { … } finally { System.out.println(“PerfExample.foo = ”+ System.nanoTime() - $start)); } } }
  • プロジェクトの作り方 prototype: https://github.com/alexruiz/dw-lombok  プロジェクト名などを置換  Ivyの設定を一部変更  ECJのjarが取得出来なかった  Lombokの最新(0.10.4)を使いたかった
  • アノテーションの定義 トリガーとなるPerfアノテーションを作成する @Target({ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) public @interface Perf { }
  • AnnotationHandlerの作成 Javac用とECJ用の二つのAnnotationHandlerを作成する // for javac package localhost.javac.handlers; @ProviderFor(JavacAnnotationHandler.class) public class HandlePerf extends JavacAnnotationHandler<Perf> { @Override public void handle(AnnotationValues<Perf> annotation, JCAnnotation ast, JavacNode annotationNode) { ... } }
  • AST変換処理の実装 愚直にASTを作るだけの簡単なお仕事 TreeMaker maker = methodNode.getTreeMaker(); // long $start = long t1 = System.nanoTime(); Name startName = methodNode.toName("$start"); JCExpression nanoTimeMethod = chainDotsString(methodNode, "System.nanoTime"); JCExpression nanoTimeApply = maker.Apply(List.<JCExpression>nil(), nanoTimeMethod, List.<JCExpression>nil()); JCVariableDecl startDef = setGeneratedBy(maker.VarDef(maker.Modifiers(0), startName, maker.TypeIdent(getCtcInt(TypeTags.class, "LONG")), nanoTimeApply), ast);
  • テスト 本体が用意しているテスト・インフラがそのまま使える @RunWith(DirectoryRunner.class) public class TestWithEcj implements TestParams { @Override public Compiler getCompiler() { return ECJ; } @Override public boolean printErrors() { return true; } @Override public File getBeforeDirectory() { return new File("test/transform/resource/before"); } @Override public File getAfterDirectory() { return new File("test/transform/resource/after-ecj"); } ... }
  • 拡張されたLombokの実行 jar化してClasspathに追加すれば良い $ ant dist $ javac -cp “.:lib/build/lombok.jar:dist/lombok-perf.jar” Example.java $ java -cp “.”Example
  • 感想 変換処理はわりと愚直にAST作るだけの簡単なお仕事 本体のコードこそが最高のサンプル  https://github.com/rzwitserloot/lombok/tree/master/src/co  https://github.com/rzwitserloot/lombok/tree/master/src/co
  • まとめ Lombokは皆さんのJava嫌いをちょっとだけ癒してくれます きっとScalaプログラマにもなれないJavaプログラマの皆さん Lombokを手にいれてみませんか?