Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

13,812 views

Published on

Published in: Technology, Education
  • Be the first to comment

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

  1. 1. 第3回 ありえる社内勉強会 「いわががのLombok」
  2. 2. お前だれよ? twitter: @kiris いわなが?いわがが?
  3. 3. Lombokって何? http://projectlombok.org  created by Javaの冗長性を排除する為  Roel Spilker のライブラリ 「赤唐辛子」の意味 v0.10.4 MIT license  Reinier Zwitserloot
  4. 4. Javaの冗長性って? こういうのとか class Data { private int value; public int getValue() { return value; } public void setValue(int value) { this.value = value; } }
  5. 5. Javaの冗長性って? 後、こういうのとか… InputStream in = new InputStream(args[0]); try { ... } finally { If (in != null) in.close(); }
  6. 6. Javaの冗長性って? 他にも、こういうのとか… Map<String, List<String>> map = new HashMap<String, List<String>>(); ... for(Map.Entry<String, List<String>> entry : map) { ... }
  7. 7. 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 +“)”; }
  8. 8. Javaの冗長性って?  こうならない為のLombok! 続きはWebで!!
  9. 9. 冗長の何がいけないの? 生産性が下がる コード量が増えて読みづらくなる バグが入り込む可能性がある 死にたくなる
  10. 10. Lombokの導入
  11. 11. Lombokを入手する Download lombok.jar  http://projectlombok.org/download.html Maven or Ivy  http://projectlombok.org/mavenrepo/index.html
  12. 12. 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
  13. 13. LombokをIDEでも使う Eclipse, NetBeans なんかに対応  IDEA IntelliJはまだ未対応 java -jar lombok.jar
  14. 14. Lombokを試してみる
  15. 15. @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フィールドを引数にしたコンストラクタの生成
  16. 16. 結果の確認(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
  17. 17. @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() { … }}
  18. 18. Eclipseからも即時反映 その場でアウトラインや補完候補に表示されます
  19. 19. 他の機能 @Getter / @Setter @Getter(lazy=true) @ToString @EqualsAndHashCode @NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor @Data @Cleanup @Synchronized @SneakyThrows @Log val @Delegate http://projectlombok.org/features/index.html
  20. 20. @Getter / @Setter Getter / Setterの自動生成 @Dataよりも優先 public class GetterSetterExample { @Getter @Setter private String name; @Getter(AccessLevel.PROTECTED) private int age; }
  21. 21. @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; } }
  22. 22. @Getter(lazy=true) いわゆるメモ化 サブルーチン(関数)呼び出しの結果を保持し、再利用するこ とで、そのサブルーチンの呼び出し毎の再計算を防ぐ public class GetterLazyExample { @Getter(lazy=true) private final double[] cached = expensive(); private double[] expensive() { ... } }
  23. 23. @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() { ... } }
  24. 24. @Cleanup リソースの片付けを自動で行なう public static void main(String[] args) throws IOException { @Cleanup InputStream in = new FileInputStream(args[0]); @Cleanup("release") MyResource resource = new MyResource(); ... }
  25. 25. @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(); } }
  26. 26. @Synchronized this以外のロックオブジェクトで排他 public class SynchronizedExample { private final Object readLock = new Object(); @Synchronized private int foo() { return 1; } @Syncrhonized("readLock") private int bar() { return 2; } }
  27. 27. @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; } } }
  28. 28. val ローカル変数の型宣言を省略 public static void main(String[] args) { val map = new HashMap<String, List<String>>(); ... for(val entry : map.entrySet()) { ... } }
  29. 29. 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()) { ... } }
  30. 30. etc @ToString  toStringの生成 @EqualsAndHashCode  equalsとhachCodeの生成 @NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor  コンストラクタの生成 @SneakyThrows  チェック例外を非チェック例外のようにthrowする @Delegate  移譲処理の生成 http://projectlombok.org/features/index.html
  31. 31. Lombokのメリット コードの冗長性の排除  生産性が上がる  コードの見通しを良くなる  バグを埋め込む可能性を減らす  心の平穏
  32. 32. Lombokのデメリット 魔法に見える(not WYSIWYG) Lombokのバグに悩まされる可能性がある リファクタリング機能との衝突 デバッグがややこしくなる
  33. 33. ここまでのまとめ LombokはJavaの冗長性を排除する Lombokの導入はとても簡単 魔法には代償をともなう
  34. 34. Break time
  35. 35. Lombokの仕組み
  36. 36. ソースコード生成?バイトコード生成? いいえ、AST変換です JavaのASTを直接生成・変換してます  ソースコード生成と違ってコードが膨れあがりません  バイトコード生成と違って同じコンパイル単位のクラスか らも可視的です
  37. 37. Lombokが保持するAST JavacとECJの二つのASTを別々に保持 AnnotationHandlerも各AST毎に実装する必要がある 二つのASTを統合するためのプロジェクトも進行中  https://github.com/rzwitserloot/lombok.ast
  38. 38. Lombokの処理の流れ
  39. 39. エントリーポイント lombok.javac.apt.Processor  implements javax.annotation.processing.Processor lombok.eclipse.TransformEclipseAST  EclipseのParserにパッチを当てて実行  https://github.com/rzwitserloot/lombok.patcher  OSGi ClassLoaderに注入されて実行される
  40. 40. AnnotationHandlerの読み込み プラグイン形式の読み込み @ProviderFor(JavacAnnotationHandler.class)  used SPI(http://code.google.com/p/spi/)  Service Provider Interfaceのwrapper
  41. 41. ASTの探索 ASTをトラバースしてアノテーションを探索 アノテーションが見付かったら、 対応するAnnotationHandlerのhandleを実行する AnnotationVisitor  Implements JavacASTVisitor  独自のVisitorも定義可能  @ProviderFor(JavacASTVisitor.class)  HandleVal
  42. 42. ASTの変換 各AnnotationHandlerや各ASTVisitorで 変換にはJavacなどの非公開APIを直接使用  com.sun.tools.javac.tree  org.eclipse.jdt.internal
  43. 43. Lombokを拡張する
  44. 44. Lombokを拡張するには? Lombokは外からの拡張を意識して作っているわけではない Lombok本体を模範することで拡張することは出来る
  45. 45. @Perf メソッドの実行時間を出力 public class PerfExample { @Perf void foo() { ... } }
  46. 46. @Perf(変換後) メソッドの実行時間を出力 public class PerfExample { void foo() { long $start = System.nanoTime(); try { … } finally { System.out.println(“PerfExample.foo = ”+ System.nanoTime() - $start)); } } }
  47. 47. プロジェクトの作り方 prototype: https://github.com/alexruiz/dw-lombok  プロジェクト名などを置換  Ivyの設定を一部変更  ECJのjarが取得出来なかった  Lombokの最新(0.10.4)を使いたかった
  48. 48. アノテーションの定義 トリガーとなるPerfアノテーションを作成する @Target({ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) public @interface Perf { }
  49. 49. 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) { ... } }
  50. 50. 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);
  51. 51. テスト 本体が用意しているテスト・インフラがそのまま使える @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"); } ... }
  52. 52. 拡張されたLombokの実行 jar化してClasspathに追加すれば良い $ ant dist $ javac -cp “.:lib/build/lombok.jar:dist/lombok-perf.jar” Example.java $ java -cp “.”Example
  53. 53. 感想 変換処理はわりと愚直にAST作るだけの簡単なお仕事 本体のコードこそが最高のサンプル  https://github.com/rzwitserloot/lombok/tree/master/src/co  https://github.com/rzwitserloot/lombok/tree/master/src/co
  54. 54. まとめ Lombokは皆さんのJava嫌いをちょっとだけ癒してくれます きっとScalaプログラマにもなれないJavaプログラマの皆さん Lombokを手にいれてみませんか?

×