the why and how of java8 at line fukuoka

113
The Why and How of Java8 at LINE Fukuoka @youhei Today's hashtag: #LINE_DM

Upload: youhei-nitta

Post on 12-Jul-2015

1.227 views

Category:

Engineering


2 download

TRANSCRIPT

Page 1: The Why and How of Java8 at LINE Fukuoka

The Why and How of Java8at LINE Fukuoka

@youhei

Today's hashtag: #LINE_DM

Page 2: The Why and How of Java8 at LINE Fukuoka

⾃⼰紹介$ whoami新⽥ 洋平2014年9⽉⼊社LINE ファミリーアプリサーバーサイド開発担当ちょっと前は Python や AWS と戯れてましたtwitter: @youhei他もだいたい youhei

Page 3: The Why and How of Java8 at LINE Fukuoka

今⽇これから話すことLINE Fukuoka の現状なぜ Java を使うようになったかなぜ Java8 を選んだのかどうやって Java8 で開発しているか実際使ってきてどうだったか

Page 4: The Why and How of Java8 at LINE Fukuoka

LINE Fukuoka の現状

Page 5: The Why and How of Java8 at LINE Fukuoka

福岡で作ってる LINE Family Apps

LINE 占いLINE MALL

LINE Creaters Market

Page 6: The Why and How of Java8 at LINE Fukuoka

サーバサイドは全部 PerlLINE Family App は Perl が主⼒です

Page 7: The Why and How of Java8 at LINE Fukuoka

2014/9 ⼊社間も無い頃「Java でやるプロジェクトがあるので、

youhei さんやってみます?」

Page 8: The Why and How of Java8 at LINE Fukuoka

Java?

Page 9: The Why and How of Java8 at LINE Fukuoka

Perl じゃないの?

Page 10: The Why and How of Java8 at LINE Fukuoka

ということで

Page 11: The Why and How of Java8 at LINE Fukuoka

六年ぶりにJava はじめました

Page 12: The Why and How of Java8 at LINE Fukuoka

そもそもなぜ Java なのかWhy we use Java?

Page 13: The Why and How of Java8 at LINE Fukuoka

まずは社内の状況確認

Page 14: The Why and How of Java8 at LINE Fukuoka

確認結果弊社 GitHub:enterprise で⼈気最上位の⾔語は Java

Spring を使った Java のプロジェクト多数LINE バックエンドでも⼤活躍Sonatype Nexus でライブラリを管理Jenkins もガンガン使ってる

Page 15: The Why and How of Java8 at LINE Fukuoka

めっちゃ Java 使ってた

Page 16: The Why and How of Java8 at LINE Fukuoka

知らなかっただけでめっちゃ Java の会社でした

Page 17: The Why and How of Java8 at LINE Fukuoka

じゃあなぜ Java 8 なのかWhy we choose Java 8?

Page 18: The Why and How of Java8 at LINE Fukuoka

プロジェクトアサイン当初つくるもの: JSON-RPC Server

ライブラリの選択は⾃由運⽤上の制約さえ守れば良くしがらみも少ない「よし、」

Page 19: The Why and How of Java8 at LINE Fukuoka

「できる限り新しいものを使おう」

Page 20: The Why and How of Java8 at LINE Fukuoka

なぜ新しいもの?

Page 21: The Why and How of Java8 at LINE Fukuoka
Page 22: The Why and How of Java8 at LINE Fukuoka

2006 年頃のJava から LL への流れ「Java だとさくさく作れないから LL へ」

元の⽊阿弥にならないために「さくさく作れる Java」でないといけな

い「重厚⻑⼤」は NG

Page 23: The Why and How of Java8 at LINE Fukuoka

社内をみるとJava 8 で先⾏している Project があった「Java で 1 から 10 まで書いた話」参照Perl をずっと書いてきた⼈にも馴染みや

すい Java

Page 24: The Why and How of Java8 at LINE Fukuoka

これはすごい

Page 25: The Why and How of Java8 at LINE Fukuoka

乗るしかないこのビッグウェーブに

Page 26: The Why and How of Java8 at LINE Fukuoka

その結果、

Page 27: The Why and How of Java8 at LINE Fukuoka

現在の構成はこうなりました

Page 28: The Why and How of Java8 at LINE Fukuoka

使っているモジュール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

Page 29: The Why and How of Java8 at LINE Fukuoka

構成図

Page 30: The Why and How of Java8 at LINE Fukuoka
Page 31: The Why and How of Java8 at LINE Fukuoka

この構成で

Page 32: The Why and How of Java8 at LINE Fukuoka

どうやって Java8 で開発しているか

How we use Java8?

Page 33: The Why and How of Java8 at LINE Fukuoka

よく使う Java 8 の新機能1. Optional2. lambda3. default method4. Stream API

Page 34: The Why and How of Java8 at LINE Fukuoka

よく使う Java 8 の新機能1. Optional2. lambda3. default method4. Stream API

Page 35: The Why and How of Java8 at LINE Fukuoka

Optional値が含まれている場合も

含まれていない場合もあるコンテナ・オブジェクト

Page 36: The Why and How of Java8 at LINE Fukuoka

コンテナ・オブジェクト配列, List, Map などのデータ構造の総称

Page 37: The Why and How of Java8 at LINE Fukuoka

ランタイムに null 参照をさけるためのコンテナが

Page 38: The Why and How of Java8 at LINE Fukuoka

Optional

Page 39: The Why and How of Java8 at LINE Fukuoka

Optional の基本「値がないかも」を明⽰して NullPointerException をさける

チェック漏れはコンパイラがチェックする

// 値がないと nobody// Optional<String> maybeNameString name = maybeName.orElse("nobody");// 値があると someMethod(d) を処理// Optional<T> datadata.ifPresent(d -> someMethod(d));

Page 40: The Why and How of Java8 at LINE Fukuoka

Real World Exampleavans, tinyorm のコード例

Page 41: The Why and How of Java8 at LINE Fukuoka

avans で Query Parameter が任意かどうかを明⽰する

利点はコード上に仕様が明記される、デフォルト値の考慮漏れもなくなる、の⼆点。

@GET("/api/items")public WebResponse list(@Param("page") OptionalInt page) { int pageNumber = page.orElse(1); // デフォルト値は 1

Page 42: The Why and How of Java8 at LINE Fukuoka

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!

Page 43: The Why and How of Java8 at LINE Fukuoka

Optional まとめ安全なコードが書けるようになるコードのドキュメント性があがるWeb層、DB層の API が対応しているとより強⼒

Page 44: The Why and How of Java8 at LINE Fukuoka

よく使う Java 8 の新機能1. Optional2. lambda3. default method4. Stream API

Page 45: The Why and How of Java8 at LINE Fukuoka

ラムダ式は⼀種の糖⾐構⽂

Page 46: The Why and How of Java8 at LINE Fukuoka

匿名クラスのインスタンス⽣成の

コードを置き換えられるイメージ

Page 47: The Why and How of Java8 at LINE Fukuoka

ただ匿名クラスのインスタンス⽣成の糖⾐構⽂ではない。

ラムダ式は匿名クラスとは異なるバイトコードを吐くinvokedynamic で実⾏時にラムダオブジェクトを⽣成する処理に置き換わる匿名クラスファイル(Foo$1.class)を作らずに済むし、インスタンス⽣成のコストも下がる

Page 48: The Why and How of Java8 at LINE Fukuoka

ラムダ式の基本

Page 49: The Why and How of Java8 at LINE Fukuoka

ラムダ式の基本(Java 7 以前)匿名クラスで関数を渡していた冗⻑な時代

辞書順にソートするコード。

// Java 7 以前の⽂法に沿った表現Collections.sort(lists, new Comparator<String>() { public int compare(final String o1, String o2) { return o1.compareTo(o2); }});

Page 50: The Why and How of Java8 at LINE Fukuoka

ラムダ式の基本(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);

Page 51: The Why and How of Java8 at LINE Fukuoka

実質的final匿名クラスではエンクロージングクラス(匿名クラスを宣⾔しているクラス)の変数が final でない場合はアクセスすることは⾔語仕様上できなかった。ラムダ式とこの仕様は相性が良くない。そこで、ラムダ式でエンクロージングクラスの変数を利⽤した場合はその変数を「実質的 final」と定義した。その変数は final が付いているものとしてみなされる。変数に再代⼊してしまうと実質的 final ではなくなるのでラムダ式で使おうとするとコンパイルエラーになる。

Page 52: The Why and How of Java8 at LINE Fukuoka

実質的final(コード例)familyName に値を代⼊するとコンパイルエラー。知らないと⼤変ハマる。

ラムダ式内での値の変更もコンパイルエラー。

String familyName = "Isono"; // 実質的 finalfamily.forEach( firstName -> { System.out.println( String.format("%s %s", firstName, familyName) });// familyName = "Fuguta"; このコメントを外すとコンパイルエラー

Page 53: The Why and How of Java8 at LINE Fukuoka

以上がラムダ式の⾔語仕様ちょっと覚えることが多い

Page 54: The Why and How of Java8 at LINE Fukuoka

Real World Exampleavans, tinyorm のコード例

Page 55: The Why and How of Java8 at LINE Fukuoka

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("こんにちは", "世界")); } });}

Page 56: The Why and How of Java8 at LINE Fukuoka

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; });

Page 57: The Why and How of Java8 at LINE Fukuoka

lambda まとめ特殊な処理をシンプルに局所化できる(関⼼の分離)

リソース解放を意識しないコードを強制できるWeb層、DB層のライブラリが対応してるとより強⼒

Page 58: The Why and How of Java8 at LINE Fukuoka

よく使う Java 8 の新機能1. Optional2. lambda3. default method4. Stream API

Page 59: The Why and How of Java8 at LINE Fukuoka

default method の基本interface に default 実装を定義できるようになった。さらっと扱われがちだけどとても⼤きな変更点。

interface Person { String getFirstName; String getFamilyName; default String getFullName() { return String.format("%s %s", getFirstName(), getFamilyName()); }}

Page 60: The Why and How of Java8 at LINE Fukuoka

interface static methodstatic method も定義できるようになった。

Collections, Paths のようなユーティリティメソッドを集めたコンパニオンクラスを定義する理由は「これまでの慣習を尊重する」以外になくなった。

Collection, Path に static method を定義可能になったため。

interface Person { static Person of() { return new DefaultPerson("Foo", "Bar"); }}

Page 61: The Why and How of Java8 at LINE Fukuoka

Real World Exampleavans のコード例

Page 62: The Why and How of Java8 at LINE Fukuoka

avans plugin を実装するavans の plugin は Controller への Mix-in となっている。default method で実装する。

interface は状態を持てないのでその部分を PluginStash として avans が提供している。

// avans pluginpublic 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; } // 以下、省略// Controllerpublic class FooController implements SessionMixin {

Page 63: The Why and How of Java8 at LINE Fukuoka

default method まとめinterface に実装を持てることで継承で実装を追加できる必ずしも「継承よりコンポジション」ではなくなったMix-in は Annotation と並んでフレームワークが拡張性を提供する優れた⼿段

Page 64: The Why and How of Java8 at LINE Fukuoka

よく使う Java 8 の新機能1. Optional2. lambda3. default method4. Stream API

Page 65: The Why and How of Java8 at LINE Fukuoka

Stream API とは

Page 66: The Why and How of Java8 at LINE Fukuoka

Collection を宣⾔的に操作できる API

Page 67: The Why and How of Java8 at LINE Fukuoka

ここまで出てきたOptional

lambda

default method

を駆使している

Page 68: The Why and How of Java8 at LINE Fukuoka

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);}

Page 69: The Why and How of Java8 at LINE Fukuoka

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);}

Page 70: The Why and How of Java8 at LINE Fukuoka

⼀時的な値が不要で再代⼊がない

Page 71: The Why and How of Java8 at LINE Fukuoka

宣⾔的で間違いも起こりにくい

Page 72: The Why and How of Java8 at LINE Fukuoka

ただ、必ずしも短く書けるわけでもない

Page 73: The Why and How of Java8 at LINE Fukuoka

Stream API のインパクトは意外と⼩さいStream を API で受け渡すことは少ないメソッド内の処理がシンプルになるのみ

Page 74: The Why and How of Java8 at LINE Fukuoka

Stream API と拡張 for ⽂の関係Stream API の出現で「forEach を使えば for ⽂不要」という話題もあるが lambda の中でチェック例外を扱うよりは、拡張 for ⽂にしてチェック例外はフレームワークに任せた⽅が可読性が⾼いこともあるWeb アプリはチェック例外も Internal Server Error で処理しておしまい、というケースが多いので、現在は Stream API ⼀辺倒にはならずに拡張 for ⽂も使ってる

Page 75: The Why and How of Java8 at LINE Fukuoka

Stream API まとめ型安全にある程度すっきり書ける(LL よりは冗⻑だけど)

宣⾔的に書けるのでバグを埋め込みにくいとはいえ拡張 for ⽂も使ってる

Page 76: The Why and How of Java8 at LINE Fukuoka

以上が Java 8 の新機能を使ってみての所感

Page 77: The Why and How of Java8 at LINE Fukuoka

ただ、

Page 78: The Why and How of Java8 at LINE Fukuoka

もっとシンプルさが欲しい

Page 79: The Why and How of Java8 at LINE Fukuoka

そこで

Page 80: The Why and How of Java8 at LINE Fukuoka

http://projectlombok.org

Page 81: The Why and How of Java8 at LINE Fukuoka

with lombokimport lombok.NonNull;public class NonNullExample extends Something { private String name; public NonNullExample(@NonNull Person person) { super("Hello"); this.name = person.getName(); }}

Page 82: The Why and How of Java8 at LINE Fukuoka

Vanilla Javaimport 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(); }}

Page 83: The Why and How of Java8 at LINE Fukuoka

アノテーションで退屈なコードを減らす

Page 84: The Why and How of Java8 at LINE Fukuoka

そもそもなぜこんなことが可能か

Page 85: The Why and How of Java8 at LINE Fukuoka

lombok の仕組み

Page 86: The Why and How of Java8 at LINE Fukuoka

通常のコンパイルの流れjavac や Eclipse Compiler for Java がソースコードを解釈して抽象

構⽂⽊を⽣成し、バイトコードにコンパイルする

1. ソースコード(.java)2. 抽象構⽂⽊3. バイトコード(.class)

Page 87: The Why and How of Java8 at LINE Fukuoka

lombok の仕組みコンパイル時に AnnotationProcessor を利⽤して取得した抽象構⽂

⽊を変換している。internal な API を呼んでいるとか。

1. ソースコード(.java)2. 抽象構⽂⽊抽象構⽂⽊ ← ココを横取りして書き換え3. バイトコード(.class)

Page 88: The Why and How of Java8 at LINE Fukuoka

この仕組みは何が嬉しいか?

Page 89: The Why and How of Java8 at LINE Fukuoka

ソースコード⽣成と違って取り扱いがラク

Page 90: The Why and How of Java8 at LINE Fukuoka

良くも悪くも痕跡が残らない

Page 91: The Why and How of Java8 at LINE Fukuoka

バイトコード変換と違って実⾏時に魔法はない

Page 92: The Why and How of Java8 at LINE Fukuoka

よく使う Annotation1. @Data2. @Value3. @SneakyThrows

Page 93: The Why and How of Java8 at LINE Fukuoka

@DataJavaBeans の getter/setter, toString, hashCode, equals を⽣成する。

Jackson で受け渡しする JSON オブジェクトなどに多⽤してる

@Datapublic class ContactForm { @Email // tinyvalidator private String email; @NotNull // tinyvalidator private String body;}

Page 94: The Why and How of Java8 at LINE Fukuoka

@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;}

Page 95: The Why and How of Java8 at LINE Fukuoka

@SneakyThrowsチェック例外を⾮チェック例外にして投げる。

Internal Server Error を返すしかないような例外はチェックせず SneakyThrows を使う。ただ最近は SneakyThrows を使わず throws 節を宣⾔するのがトレンドになってきた。

@POST("/upload")@SneakyThrowspublic WebResponse upload(@UploadFile("file") Part file) { String body = IOUtils.toString( file.getInputStream(), "UTF-8"); return this.renderText(body);}

Page 96: The Why and How of Java8 at LINE Fukuoka

lombok.valfinal なローカル変数を短く宣⾔できる。使おうかと思ったが、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<>();

Page 97: The Why and How of Java8 at LINE Fukuoka

F.A.Q.

Page 98: The Why and How of Java8 at LINE Fukuoka

Q. テストは?Model のテストと Controller のテストをしてる。

Model のテストは MySQL をそのまま使う。

Controller は tomcat-embed を起動して HTTP Client を使う。

いわゆる end-to-end なテスト。

別サービスへのアクセスは tomcat-embed と avans で Mock を書く。

Page 99: The Why and How of Java8 at LINE Fukuoka

Q. 静的解析とかしてる?checkstyle, findbugs を使ってる。

jenkins で動かしてる。

Checkstyle は Java8 対応版を社内ビルドして使ってる。

lombok.GetterLazy が FindBugs で怒られる...

Page 100: The Why and How of Java8 at LINE Fukuoka

Q. 社内に Author がいるWeb Framework の利点とは

?

Page 101: The Why and How of Java8 at LINE Fukuoka

現場のニーズに完璧にフィットする

必要にかられて作られている。最低限の必要な機能を提供している。

⾜りない機能は Mix-in, 独⾃ Annotation で⾃分たちで拡張できる。

Page 102: The Why and How of Java8 at LINE Fukuoka

余分なコードがほぼゼロで⾒通しが良い

Spring Boot や Dropwizard より軽量また、どんな汎⽤ micro framework にも使わない機能のためのコード、使わないミドルウェアのためのコードは必ずあるもの。

Page 103: The Why and How of Java8 at LINE Fukuoka

実装の意図が把握しやすい直接聞けることは良いことだ。

⾮互換の変更が⼊るとすぐアナウンスをもらえる。

Page 104: The Why and How of Java8 at LINE Fukuoka

Q. avans, tinyorm ⾃体の良さとは?

Page 105: The Why and How of Java8 at LINE Fukuoka

Java 8 を前提としているサービスのコードが⾃然と Java 8 らしいコードに矯正される。

Page 106: The Why and How of Java8 at LINE Fukuoka

依存がヒッジョーに少ない

Page 107: The Why and How of Java8 at LINE Fukuoka

よって外部要因に左右されにくい

最悪⾃分たちでなんとかできる、というビジネス⾯でのメリット。

再び歩み始めた Java の継続的進化に追随しやすい、という技術⾯でのメリット。

Page 108: The Why and How of Java8 at LINE Fukuoka

覚えることが少ない別のことに時間を使える

Java SE 8 の⾔語仕様を知ったり、Servlet 3.x API を把握したり。良いコードの書き⽅を知ることに時間を割こう。

Page 109: The Why and How of Java8 at LINE Fukuoka

最後に

Page 110: The Why and How of Java8 at LINE Fukuoka

これは現時点でのスナップショット

弊社で Java 8 の本格利⽤は始まったばかり

現在もより良い構成をブラッシュアップしてる最中

紆余曲折を現場で体験出来ているのはエンジニアとしてとても幸せ

Page 111: The Why and How of Java8 at LINE Fukuoka

LINE Family App の Java は始まったばかり

やらなきゃいけないことも、やりたいこともまだまだたくさんだからこそ楽しい

Page 112: The Why and How of Java8 at LINE Fukuoka

ご清聴ありがとうございました

Page 113: The Why and How of Java8 at LINE Fukuoka

参考資料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