Advertisement
Advertisement

More Related Content

Similar to The Why and How of Java8 at LINE Fukuoka(20)

Advertisement

The Why and How of Java8 at LINE Fukuoka

  1. The Why and How of Java8 at LINE Fukuoka @youhei Today's hashtag: #LINE_DM
  2. ⾃⼰紹介 $ whoami 新⽥ 洋平 2014年9⽉⼊社 LINE ファミリーアプリサーバーサイド開発担当 ちょっと前は Python や AWS と戯れてました twitter: @youhei 他もだいたい youhei
  3. 今⽇これから話すこと LINE Fukuoka の現状 なぜ Java を使うようになったか なぜ Java8 を選んだのか どうやって Java8 で開発しているか 実際使ってきてどうだったか
  4. LINE Fukuoka の現状
  5. 福岡で作ってる LINE Family Apps LINE 占い LINE MALL LINE Creaters Market
  6. サーバサイドは全部 Perl LINE Family App は Perl が主⼒です
  7. 2014/9 ⼊社間も無い頃 「Java でやるプロジェクトがあるので、 youhei さんやってみます?」
  8. Java?
  9. Perl じゃないの?
  10. ということで
  11. 六年ぶりに Java はじめました
  12. そもそもなぜ Java なのか Why we use Java?
  13. まずは社内の状況確認
  14. 確認結果 弊社 GitHub:enterprise で⼈気最上位の⾔ 語は Java Spring を使った Java のプロジェクト多 数 LINE バックエンドでも⼤活躍 Sonatype Nexus でライブラリを管理 Jenkins もガンガン使ってる
  15. めっちゃ Java 使ってた
  16. 知らなかっただけで めっちゃ Java の会社でした
  17. じゃあなぜ Java 8 なのか Why we choose Java 8?
  18. プロジェクトアサイン当初 つくるもの: JSON-RPC Server ライブラリの選択は⾃由 運⽤上の制約さえ守れば良く しがらみも少ない 「よし、」
  19. 「できる限り新しいものを 使おう」
  20. なぜ新しいもの?
  21. 2006 年頃の Java から LL への流れ 「Java だとさくさく作れないから LL へ」 元の⽊阿弥にならないために 「さくさく作れる Java」でないといけな い 「重厚⻑⼤」は NG
  22. 社内をみると Java 8 で先⾏している Project があった 「Java で 1 から 10 まで書いた話」参照 Perl をずっと書いてきた⼈にも馴染みや すい Java
  23. これはすごい
  24. 乗るしかない このビッグウェーブに
  25. その結果、
  26. 現在の構成は こうなりました
  27. 使っているモジュール avans - Tiny thin web application framework for Java 8 webscrew - Web application toolkit for Java servlet tinyorm - O/R mapper for Java 8 tinyvalidator - Tiny validation framework mech2 - HTTP client jackson - Json parsing and generation lombok - Reduce boilerplate code 緑字は社内に Author がいる OSS, ⿊字はそうでない OSS
  28. 構成図
  29. この構成で
  30. どうやって Java8 で開発し ているか How we use Java8?
  31. よく使う Java 8 の新機能 1. Optional 2. lambda 3. default method 4. Stream API
  32. よく使う Java 8 の新機能 1. Optional 2. lambda 3. default method 4. Stream API
  33. Optional 値が含まれている場合も 含まれていない場合もあるコンテナ・オブジェクト
  34. コンテナ・オブジェクト 配列, List, Map などのデータ構造の総称
  35. ランタイムに null 参照をさ けるためのコンテナが
  36. Optional
  37. Optional の基本 「値がないかも」を明⽰して NullPointerException をさける チェック漏れはコンパイラがチェックする // 値がないと nobody // Optional<String> maybeName String name = maybeName.orElse("nobody"); // 値があると someMethod(d) を処理 // Optional<T> data data.ifPresent(d -> someMethod(d));
  38. Real World Example avans, tinyorm のコード例
  39. avans で Query Parameter が任意かどうか を明⽰する 利点はコード上に仕様が明記される、デフォルト値の考慮漏れもなくなる、の⼆点。 @GET("/api/items") public WebResponse list(@Param("page") OptionalInt page) { int pageNumber = page.orElse(1); // デフォルト値は 1
  40. tinyorm で単⼀⾏の削除 SELECT FOR UPDATE した結果がある場合のみ DELETE を発⾏ TinyORM db = TinyORM(connection); db.single(ItemRow.class) .where("id=?", id) .forUpdate() .execute() // Optional<ItemRow> を返す .ifPresent(row -> row.delete()); // ItemRow がある場合だけ DELETE!
  41. Optional まとめ 安全なコードが書けるようになる コードのドキュメント性があがる Web層、DB層の API が対応しているとよ り強⼒
  42. よく使う Java 8 の新機能 1. Optional 2. lambda 3. default method 4. Stream API
  43. ラムダ式は⼀種の糖⾐構⽂
  44. 匿名クラスのインスタンス ⽣成の コードを置き換えられる イメージ
  45. ただ匿名クラスのインスタンス⽣成の 糖⾐構⽂ではない。 ラムダ式は匿名クラスとは異なるバイトコードを吐く invokedynamic で実⾏時にラムダオブジェクトを⽣成する処理に 置き換わる 匿名クラスファイル(Foo$1.class)を作らずに済むし、インスタン ス⽣成のコストも下がる
  46. ラムダ式の基本
  47. ラムダ式の基本(Java 7 以前) 匿名クラスで関数を渡していた冗⻑な時代 辞書順にソートするコード。 // Java 7 以前の⽂法に沿った表現 Collections.sort(lists, new Comparator<String>() { public int compare(final String o1, String o2) { return o1.compareTo(o2); } });
  48. ラムダ式の基本(Java 8 ラムダ式) 同じコードをラムダ式で書く。多くの要素を省略できる。 // 省略なし Collections.sort(lists, (final String o1, final String o2) -> { return o1.compareTo(o2); }); // 処理が1⾏だとコードブロックの波括弧と return は省略可 Collections.sort(lists, (final String o1, final String o2) -> o1.compareTo(o2)); // 型推論で引数の型宣⾔を省略可。final の明⽰はできなくなる。 // (引数が⼀個の場合、引数の括弧も省略可) Collections.sort(lists, (o1, o2) -> o1.compareTo(o2)); // メソッド参照 Collections.sort(lists, String::compareTo);
  49. 実質的final 匿名クラスではエンクロージングクラス(匿名クラスを宣⾔してい るクラス)の変数が final でない場合はアクセスすることは⾔語仕 様上できなかった。 ラムダ式とこの仕様は相性が良くない。そこで、 ラムダ式でエンクロージングクラスの変数を利⽤した場合はその 変数を「実質的 final」と定義した。その変数は final が付いてい るものとしてみなされる。 変数に再代⼊してしまうと実質的 final ではなくなるのでラムダ式 で使おうとするとコンパイルエラーになる。
  50. 実質的final(コード例) familyName に値を代⼊するとコンパイルエラー。知らないと⼤変ハマる。 ラムダ式内での値の変更もコンパイルエラー。 String familyName = "Isono"; // 実質的 final family.forEach( firstName -> { System.out.println( String.format("%s %s", firstName, familyName) }); // familyName = "Fuguta"; このコメントを外すとコンパイルエラー
  51. 以上がラムダ式の⾔語仕様 ちょっと覚えることが多い
  52. Real World Example avans, tinyorm のコード例
  53. avans で CSV を返す HttpServletResponse に依存した処理をラムダ式内で扱う。 CallbackResponse のラムダ式は csv() 内ではなく avans に処理を返してから遅延実⾏される。 @GET("/csv") public WebResponse csv() { return new CallbackResponse(resp -> { resp.setContentType("text/csv; charset=UTF-8"); resp.setHeader("Content-Disposition", "attachment;filename=my-file-name.csv"); CSVFormat format = CSVFormat.EXCEL; try (Writer writer = resp.getWriter(); CSVPrinter csvPrinter = new CSVPrinter(writer, format)) { csvPrinter.printRecord( Arrays.asList("こんにちは", "世界")); } }); }
  54. tinyorm で 複雑なクエリをマッピングする GROUP BY したり JOIN する複雑なクエリは SQL を直接書くポリシー。 ResultSet をラムダ式内で扱う。 db.executeQuery( "SELECT count(*) as cnt FROM item GROUP BY group_id", rs -> { List<Long> result = new ArrayList<>(); while (rs.next()) { result.add(rs.getLong("cnt")); } return result; });
  55. lambda まとめ 特殊な処理をシンプルに局所化できる (関⼼の分離) リソース解放を意識しないコードを強制 できる Web層、DB層のライブラリが対応してる とより強⼒
  56. よく使う Java 8 の新機能 1. Optional 2. lambda 3. default method 4. Stream API
  57. default method の基本 interface に default 実装を定義できるようになった。 さらっと扱われがちだけどとても⼤きな変更点。 interface Person { String getFirstName; String getFamilyName; default String getFullName() { return String.format("%s %s", getFirstName(), getFamilyName()); } }
  58. interface static method static method も定義できるようになった。 Collections, Paths のようなユーティリティメソッドを集めたコンパニオンクラスを 定義する理由は「これまでの慣習を尊重する」以外になくなった。 Collection, Path に static method を定義可能になったため。 interface Person { static Person of() { return new DefaultPerson("Foo", "Bar"); } }
  59. Real World Example avans のコード例
  60. avans plugin を実装する avans の plugin は Controller への Mix-in となっている。default method で実装する。 interface は状態を持てないのでその部分を PluginStash として avans が提供している。 // avans plugin public interface SessionMixin extends Controller { static final String STASH_KEY = "session"; public default WebSessionManager getSession() { final Object session = this.computePluginStashValueIfAbsent( this.getClass(), STASH_KEY, () -> { return this.buildSessionManager(); }); return (WebSessionManager) session; } // 以下、省略 // Controller public class FooController implements SessionMixin {
  61. default method まとめ interface に実装を持てることで継承で実 装を追加できる 必ずしも「継承よりコンポジション」で はなくなった Mix-in は Annotation と並んでフレームワ ークが拡張性を提供する優れた⼿段
  62. よく使う Java 8 の新機能 1. Optional 2. lambda 3. default method 4. Stream API
  63. Stream API とは
  64. Collection を宣⾔的に操作で きる API
  65. ここまで出てきた Optional lambda default method を駆使している
  66. Stream API の基本 - 従来の命令型スタイル 価格が20ドルより⼤きい場合は10%引きして値引きした商品の⾦額合計を計算 public static void main(final String... args) { int[] prices = {10, 20, 30, 40}; int totalOfDiscountedPrices = 0; for(int price : prices) { if (price > 20) { totalOfDiscountedPrices = totalOfDiscountedPrices + (int) (price * 0.9); } } System.out.println("Total of discounted prices: " + totalOfDiscountedPrices); }
  67. Stream API の基本 - 関数型スタイル 同様の計算を関数型スタイルにする public static void main(final String... args) { int[] prices = {10, 20, 30, 40}; int totalOfDiscountedPrices = Arrays.stream(prices) // IntStream に変換 .filter(price -> price > 20) // 20 を超えるものを抜き出す .map(price -> (int) (price * 0.9)) // 0.9 掛けした値に変換 .sum(); // 合計する System.out.println("Total of discounted prices: " + totalOfDiscountedPrices); }
  68. ⼀時的な値が不要で再代⼊ がない
  69. 宣⾔的で間違いも起こりに くい
  70. ただ、必ずしも短く書ける わけでもない
  71. Stream API のインパクトは意外と⼩さい Stream を API で受け渡すことは少ない メソッド内の処理がシンプルになるのみ
  72. Stream API と拡張 for ⽂の関係 Stream API の出現で「forEach を使えば for ⽂不要」という話題 もあるが lambda の中でチェック例外を扱うよりは、拡張 for ⽂ にしてチェック例外はフレームワークに任せた⽅が可読性が⾼い こともある Web アプリはチェック例外も Internal Server Error で処理してお しまい、というケースが多いので、現在は Stream API ⼀辺倒に はならずに拡張 for ⽂も使ってる
  73. Stream API まとめ 型安全にある程度すっきり書ける(LL よ りは冗⻑だけど) 宣⾔的に書けるのでバグを埋め込みにく い とはいえ拡張 for ⽂も使ってる
  74. 以上が Java 8 の新機能を使 ってみての所感
  75. ただ、
  76. もっとシンプルさが欲しい
  77. そこで
  78. http://projectlombok.org
  79. with lombok import lombok.NonNull; public class NonNullExample extends Something { private String name; public NonNullExample(@NonNull Person person) { super("Hello"); this.name = person.getName(); } }
  80. Vanilla Java import lombok.NonNull; public class NonNullExample extends Something { private String name; public NonNullExample(@NonNull Person person) { super("Hello"); if (person == null) { throw new NullPointerException("person"); } this.name = person.getName(); } }
  81. アノテーションで 退屈なコードを減らす
  82. そもそもなぜ こんなことが可能か
  83. lombok の仕組み
  84. 通常のコンパイルの流れ javac や Eclipse Compiler for Java がソースコードを解釈して抽象 構⽂⽊を⽣成し、バイトコードにコンパイルする 1. ソースコード(.java) 2. 抽象構⽂⽊ 3. バイトコード(.class)
  85. lombok の仕組み コンパイル時に AnnotationProcessor を利⽤して取得した抽象構⽂ ⽊を変換している。internal な API を呼んでいるとか。 1. ソースコード(.java) 2. 抽象構⽂⽊ ← ココを横取りして書き換え 3. バイトコード(.class)
  86. この仕組みは何が嬉しいか?
  87. ソースコード⽣成と違って 取り扱いがラク
  88. 良くも悪くも 痕跡が残らない
  89. バイトコード変換と違って 実⾏時に魔法はない
  90. よく使う Annotation 1. @Data 2. @Value 3. @SneakyThrows
  91. @Data JavaBeans の getter/setter, toString, hashCode, equals を⽣成する。 Jackson で受け渡しする JSON オブジェクトなどに多⽤してる @Data public class ContactForm { @Email // tinyvalidator private String email; @NotNull // tinyvalidator private String body; }
  92. @Value @Data の不変オブジェクト版(setter が⽣成されない)。 java.beans.ConstructorProperties でアノテーションされたコンストラクタを⽣成する。 TinyORM は ConstructorProperties を解釈するため、Row を不変オブジェクトにできる。 @Value @Table("contact") // tinyorm @EqualsAndHashCode(callSuper = false) // 継承先を考慮しない public class ContactRow extends Row<ContactRow> { @Column // tinyorm private String email; @Column private String body; }
  93. @SneakyThrows チェック例外を⾮チェック例外にして投げる。 Internal Server Error を返すしかないような例外はチェックせず SneakyThrows を使う。 ただ最近は SneakyThrows を使わず throws 節を宣⾔するのがトレンドになってきた。 @POST("/upload") @SneakyThrows public WebResponse upload(@UploadFile("file") Part file) { String body = IOUtils.toString( file.getInputStream(), "UTF-8"); return this.renderText(body); }
  94. lombok.val final なローカル変数を短く宣⾔できる。使おうかと思ったが、IntelliJ IDEA が未対応なので断念。 Eclipse でもエラーになるという噂がちらほら。 ただ、今となってはダイヤモンド演算⼦で⼗分とも思う。 import lombok.val; public class Main { public void main(String... args) { // lombok.val val example = new ArrayList<String>(); // diamond operator final ArrayList<String> example2 = new ArrayList<>();
  95. F.A.Q.
  96. Q. テストは? Model のテストと Controller のテストをしてる。 Model のテストは MySQL をそのまま使う。 Controller は tomcat-embed を起動して HTTP Client を使う。 いわゆる end-to-end なテスト。 別サービスへのアクセスは tomcat-embed と avans で Mock を書く 。
  97. Q. 静的解析とかしてる? checkstyle, findbugs を使ってる。 jenkins で動かしてる。 Checkstyle は Java8 対応版を社内ビルドして使ってる。 lombok.GetterLazy が FindBugs で怒られる...
  98. Q. 社内に Author がいる Web Framework の利点とは ?
  99. 現場のニーズに完璧にフィ ットする 必要にかられて作られている。 最低限の必要な機能を提供している。 ⾜りない機能は Mix-in, 独⾃ Annotation で⾃分たちで拡張できる。
  100. 余分なコードがほぼゼロで ⾒通しが良い Spring Boot や Dropwizard より軽量 また、どんな汎⽤ micro framework にも使わない機能のためのコ ード、使わないミドルウェアのためのコードは必ずあるもの。
  101. 実装の意図が把握しやすい 直接聞けることは良いことだ。 ⾮互換の変更が⼊るとすぐアナウンスをもらえる。
  102. Q. avans, tinyorm ⾃体の良 さとは?
  103. Java 8 を前提としている サービスのコードが⾃然と Java 8 らしいコードに矯正される。
  104. 依存がヒッジョーに少ない
  105. よって外部要因に左右され にくい 最悪⾃分たちでなんとかできる、というビジネス⾯でのメリット 。 再び歩み始めた Java の継続的進化に追随しやすい、という技術⾯ でのメリット。
  106. 覚えることが少ない 別のことに時間を使える Java SE 8 の⾔語仕様を知ったり、Servlet 3.x API を把握したり。 良いコードの書き⽅を知ることに時間を割こう。
  107. 最後に
  108. これは現時点でのスナッ プショット 弊社で Java 8 の本格利⽤は始まったばか り 現在もより良い構成をブラッシュアップし てる最中 紆余曲折を現場で体験出来ているのはエン ジニアとしてとても幸せ
  109. LINE Family App の Java は 始まったばかり やらなきゃいけないことも、やりたいこともまだまだたくさん だからこそ楽しい
  110. ご清聴ありがとうございま した
  111. 参考資料 LL から Java に移⾏した⼈がはまりがちなこと Java で 1 から 10 まで書いた話 Java SE 8 実践プログラミング Java による関数型プログラミング Java の lambda の裏事情 ProjectLambda・2部(ラムダ式(実質的にfinal・スコープ)〜メソッド・コンストラク タ参照) Lombokのチュートリアル記事Reducing Boilerplate Code with Project Lombokをテ キトーに訳した avans, webscrew, tinyorm, tinyvalidator, mech2
Advertisement