63
ULS Copyright © 2011-2014 UL Systems, Inc. All rights reserved. Proprietary & Confidential Powered by Java8勉強会 ウルシステムズ株式会社 前多賢太郎 2014/10/25

Java8勉強会

Embed Size (px)

DESCRIPTION

2014年10月25日に実施されたJava8勉強会の資料です。

Citation preview

Page 1: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by

Java8勉強会

ウルシステムズ株式会社

前多賢太郎

2014/10/25

Page 2: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 1

はじめに

今年3月にJava8がリリースされました。

Java8には、インターフェースデフォルト実装, ラムダ式, Stream APIという設計・コーディングに大きな影響のあるアップデートが含まれます。そのためか、まだ開発の現場で採用になったという声は聞かれません。

6/25にJava8対応版 Eclipse4.4 Luna もリリースされ、 Java8による開発がいよいよ広がっていくと予想されます。

–なおNetBeansの方が完成度高いです。

– Lunaはラムダ式のコードアシストが効かない、コンパイルできないバグがあります。

というわけで、ここら辺でJava8の新機能をおさらいします。

–目玉となるラムダ式,Stream APIについて、オブジェクトとどのように組み合わせるかという事を中心に演習を実施します。

Page 3: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 2

前提事項

本日はJava8の変更点を体感するため、演習を行います。

演習を行うため事前に、jdk8, Eclipse4.4またはNetBeans8 の導入をお願いします

– IntelliJ IDEAでもかまいませんが上記の2つほど念入りに動作検証を行っていません。

演習問題はmavenプロジェクトとして以下に公開しています。

– https://github.com/enterprisegeeks/java8_study

– 上記のページの指示に従って導入をお願いします。

– 解答は上記ページのanswerブランチで、勉強会終了後に公開します。

なお、現状NetBeans8/IntelliJ IDEAの方が使い勝手が良いです。

– eclipseはコンパイラにバグがあります。

– NetBeansで編集中のファイルのmainを実行するには、Shift + F6 です。

Page 4: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 3

Javaによくあるイメージ

コードが長い・冗長

– System.out.println とか

– リストから何かを探すコードとか

– ファイルの入出力とか

– Etc,,,,

確かに、Javaのコードは堅苦しいと思うときがある。

– rubyとかLL言語に手を出すと特にそう思う。

–一方で、Java SE5以降、static import・try-with-resource句、java NIO2など簡潔に書ける言語仕様が取り入れられつつある。

Java8 ではラムダ式・Stream APIの導入により、更なるコードの簡潔さと平行性・(個人的に)楽しさを取り入れることが可能になる。

for (String s : list) {if (s.equals(“100")) {

return s;}

}

Page 5: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 4

Java8 更新機能一覧

Java8の更新機能一覧は以下で見れます。

– http://openjdk.java.net/projects/jdk8/features

更新機能のサマリは以下を見てください。http://yoshio3.com/2014/03/21/congrats-javase8-launch/

本日はラムダ式、Stream APIなど文法や新APIに焦点を絞ります。

また、Stream APIについては、以下にも詳しい説明があります。

– http://enterprisegeeks.hatenablog.com/category/Java8

Page 6: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 5

目次

ラムダ式

演習1

Stream API

演習2

演習3

Optional

インターフェース拡張

– staticメソッド実装

–デフォルトメソッド

演習4

その他トピック

–日付API

– JVMまわり

90分

90分

Page 7: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 6

ラムダ式

Page 8: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 7

ラムダ式とは

仕様

– JEP126 Lambda Expressions & Virtual Extension Methods

できるようになったこと

– ラムダ式の文法追加による(限定的な)関数型プログラムのサポート

影響

–関数型(ラムダ式)を使うと

特定の処理が、非常に簡単に書ける。

オブジェクト指向よりも、使い勝手の良いAPIを提供できる。

–一方で

Javaしかプログラム経験の無い人には、ラムダ式や関数型を理解するのは難しい。

コーディング規約でラムダ式禁止とかありえそう。

Page 9: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 8

ラムダ式って? 関数型プログラムって?

関数って?

–何らかの値を取り、何らかの値を返すもの。

ラムダ式って?

–関数の表現方法。数学由来で、関数を λx. x+2 のように書くから。

–プログラムでは、匿名関数を生成するリテラルとして使用する。

Javaにしかないの?

– Javaは後発。 Ruby, C#, C++11, javascript, Python なんでもござれ。

関数型プログラムって?

–広義には、関数を値として扱えるプログラムスタイル。(他にも色々あるが割愛)

大雑把にいうと、関数を変数、メソッド引数や戻り値に指定できる事。Javascriptの以下のような書き方が、Javaでも可能になる。

C の関数ポインタも、関数型の一種と言えなくはない。。。

var add2 = function(x){return x + 2;}add2(3) // 5

Page 10: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 9

ラムダ式の文法と型

文法

–基本書式

–省略形での記述も可能。というよりなるべく省略形を使ったほうが、良い。

引数の型は、省略可能で型は、その場合コンパイラが推測する(後述)。

引数リストは、引数が1つの場合はカッコが省略可能。(ただし引数なしは()とする )

メソッド本体はメソッドが1行の場合、{}とreturn,セミコロンを省略可能。

疑問

– どうしてラムダ式の引数の型を書かなくても良いの?戻り値の型の宣言は?コンパイルエラーや実行時エラーが起きるんじゃないの?

– ラムダ式で作った関数(値)って結局何なの?

//文字列比較の例 int copmareTo(String x, String y)(String x, String y) ->{return x.compareTo(y);}

(型 引数1, 型 引数2、、、) -> {メソッド本体}

//省略形(x,y) -> x.compareTo(y)

Page 11: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 10

ラムダ式の文法と型

型と型推論

– ラムダ式は、ラムダ式を受け取る変数や引数がセットで必要。

–それらの型は、関数型インターフェースという特定の条件を満たしたインターフェースである必要がある。

関数型インターフェースとは?

– 実装するべきメソッドが1つのみのインターフェース(staticメソッド、デフォルトメソッドは幾つあってもよい。equals等Objectのメソッドも対象外)

– Comparator(compare),Runnable(run)などが対象。

– @FunctionalInterfaceアノテーションが追加。コンパイル時に関数型インターフェースの要件を満たすかチェック可能。(チェックだけなので、アノテーションはあってもなくてもよい。)

–つまり、ラムダ式で生成されたオブジェクトは、関数型インターフェースを実装した匿名クラスのオブジェクト。

– ラムダ式で省略した引数や戻り値の型は、変数・引数の型宣言からコンパイラが推測して、合わない場合はコンパイルエラーとする。このような仕組みを型推論と呼ぶ。

Comparator<String> comp = (x,y) -> x.compareTo(y)comp.compare("aaa", "bbb"); //-1

インターフェースのメソッドがint compare(String, String) なので、x,yをStringと推測し、x,yが実行するメソッド、戻り値がintになるかをチェックする。

Page 12: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 11

ラムダ式の正体

結局のところ、Java8のラムダ式はメソッドが1つしかないインターフェースの匿名クラスを作るシンタックスシュガー

–匿名クラスとラムダ式は(ほとんど※)同じ。→5行が1行に! 5倍の生産性!

–匿名クラスと比較して、ラムダ式のほうが実際の処理のみを記述しておりノイズが少ない。

一方で、メソッド名などがないのでラムダ式に該当する変数名などの命名が重要となる。

–※匿名クラスはコンパイル時にクラスファイルが作られるが、ラムダ式ではクラスファイルは作成されない。

Invoke dynamicという新しく追加されたJVM命令を使って、実行時にインスタンス生成が行われている。

ラムダ式は多用されるので、Invoke dynamicによる最適化の余地を残している。

Comparator<String> comp = new Comparator<String>(){public int compare(String x, String y) {

return x.compareTo(y);}

};

Comparator<String> comp = (x,y) -> x.compareTo(y)

Page 13: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 12

ラムダ式の利用箇所

使いどころ

–処理の大枠が決まっていて一部の挙動を変えたいという場合、継承やインターフェース実装を使うよりもシンプルに実現できる。

イベントハンドラ、コールバック(Swingとか)

Stream API(後述)

Strategyパターン (Collections#sortとか)

Visitorパターン

–匿名クラスを受け取る前提だったAPIの引数は、ラムダ式に置き換えが可能。5倍の生産性は言いすぎだが、体感で2,3割コードが短くなる事もある。→ NetBeansはラムダ式に置換なコードを教えてくれるできたやつ。

Page 14: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 13

ラムダ式利用サンプル

既存APIや、ラムダを使う例

個人的には3番目の例のような、リソース(ファイル,JDBC)の情報を任意のオブジェクトにマッピングする方法として、ラムダ式が多用されるのではと思う。

–引数ありのコンストラクタや、toString,equals,hashCodeがちゃんと定義されているとベター。

–最後の “System.out::println”は次で解説。

//Collections#sort, Comparator#compare((T,T)->int)List<Integer> list = Arrays.asList(5,4,3,2,1);Collections.sort(list, (x, y) -> x - y);// 1,2,3,4,5

//Swing addEventListner for Button. ActionListener#actionPerformed()JButton b2 = new JButton("B");b2.addActionListener(e -> textField.setText("B clicked."));

// CSVの1行をカンマで分割した配列を、任意のオブジェクトに変換する自作メソッドList<Person> list = CSVReader.read(new File("user.csv"),

array -> new Person(array[0], array[1]));list.forEach(System.out::println);// ←何だこれ?

Page 15: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 14

メソッド参照

前述の、”System.out::println”はメソッド参照と呼ばれる新しい記法。

既存のメソッドを用いるだけのラムダ式を、関数オブジェクトとしてラムダ式の引数を省略する。

–関数プログラムでは、関数を引数に取る場所に、直接関数名を渡す事ができるので、それと似たようなものを実現している。

–以下のような場合に置き換える事ができる。

ラムダ式の引数をstaticメソッドに渡すだけの場合。

– s -> System.out.println(s) => System.out::println

ラムダ式の引数のインスタンスメソッドを呼ぶだけの場合

– s -> s.lengh() => String::length

– u -> u.getName() => User::getName

(主に)引数なしのラムダ式でコンストラクタを呼ぶだけの場合(supplier,generatorと呼ばれる)

– () -> new ArrayList() => ArrayList::new

メソッド参照を書ける必要は無いが、読める必要はある

– Javadocを見ていると当たり前にメソッド参照のコードが出てくる

メソッド参照が書けるようになると、ラムダ一人前

Page 16: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 15

メソッド参照の例

以下のラムダ式と同じ意味のメソッド参照の例を示す。

– NetBeansはメソッド参照に置き換え可能なラムダ式を教えてくれるできたやつ。

// 引数あり、戻り値なしの例list.forEach(e -> System.out.println(e));list.forEach(System.out::println);

// 引数なしの例。コンストラクタもメソッド参照可能Supplier<List<String>> s1 = () -> new ArrayList<>();Supplier<List<String>> s2 = ArrayList::new;List<String> l = s2.get(); // ArrayList

// 2引数の例。Integer.sumは加算メソッドだが、メソッド参照ために用意されたみたいだ。BinaryOperator<Integer> add1 = (a,b)-> Integer.sum(a, b);BinaryOperator<Integer> add2 = Integer::sum;add2.apply(5,6);// 11

// オブジェクトのメソッドも実行可能。インスタンスメソッドなのに、Person::となるのは正直キモイFunction<Person, String> p1 = p -> p.getName();Function<Person, String> p2 = Person::getName;

Page 17: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 16

演習1(20分)

パッケージ practice1

–対象

LambdaLession.java

– ラムダ式の書き換え

– オブジェクトとラムダ式の組み合わせ

LetsMakeHFO.java

– ラムダ式を受け取るメソッドを作ろう。

テストデータ

–テストデータは、部署-課-社員 の階層構造を持つオブジェクトです。

部署は課の一覧を取得できる。(総務部は、総務1課,総務2課などを取得可能)

課は所属する部署を取得できる。

課は課に所属する社員の一覧を取得できる。

社員は所属する課を取得できる。

TestDataは、全ての部署、全ての課、全ての社員を取得するユーティリティです。

部署 課 社員

総務 総務1課

総務2課

田中

山田営業

Page 18: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 17

標準の関数型インターフェース

既存の多くのインターフェースが関数型インターフェースになりえるが、java.util.functionパッケージに、標準的な関数型インターフェースが追加された。

– (むしろ、既存のインターフェースと関数型インターフェースを別物にできれば、本当の関数型が導入できたかもしれないが、下位互換のために諦めたような気がする)

ラムダ式を扱う前提のAPIを作成する場合に使うと良い。

Stream APIをはじめとして、標準APIにも関数型インターフェースを受け取るものが多くあるので、意味を知っておく必要がある。そうでないと、Stream APIのjavadocが理解できない。

抜粋

関数型インターフェース 引数 戻り値 用途

Function<T,R> T R TからRへの変換を行う。

Consumer<T> T void 任意の副作用を実行する。

Supplier<T> void T値Tを生成する。(型パラメータはRの方が適切だが、JavadocではT)

Predicate<T> T boolean Tから真偽値を判定する。

BiFunction<T,U,R> T,U R2つの引数T,UからRへの変換・計算を行う。 この他、Bixxxは2引数を取るという意味

Page 19: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 18

ラムダ式まとめ

ラムダ式(関数型)は、オブジェクト指向よりもコンパクトに、コードの再利用を促す

– オブジェクト指向は、フレームワークやモジュールのような大きな粒度に向いている

– 関数型は、共通関数とか簡潔なコードのような小さい部分に向いている

– 両者をバランスよく使っていきましょう。

Java8のラムダ式は、後付けかつ機能が限定的なのでちぐはぐな部分が多い。

– ラムダ式で作った関数オブジェクトの関数を呼ぶ場合に、“変数名.メソッド”とする必要があるのは微妙。(前述の add2.apply(5,6) みたいな。 Javascriptだとadd2(5,6)でいける)

–関数合成ができるが、カリー化、部分適用、遅延評価、多相、末尾再帰みたいな機能はない。

Java8でのラムダ式の使いどころは、引数に関数を渡す時

–変数や戻り値にしたところで前述の問題があるので使いづらい

– Java8で関数型プログラムにこだわりすぎると、逆に失敗する

ラムダ式導入の目的はStream APIのためで、関数型プログラムではありません

関数型プログラムをやりたいなら言語を変えましょう

Page 20: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 19

ラムダ式補足

例外と相性が悪い– もしチェック例外を投げる必要があるなら、関数型インターフェースのメソッド宣言でthrows句を付ける必要があります。

– Function等デフォルトの関数型インターフェースにはthrows句がありませんので、(Stream API)でチェック例外を扱う必要が出てくると、ラムダ式本体にtryとか書く必要があるので発狂する。

– Java.io.UncheckedIOExceptionとか、Javaの基本スタンスを崩すようなものも出てきた。

ラムダ式の外部の変数参照

–無名クラスではfinal変数に限り外部変数の参照ができた。

– ラムダ式も同様に、final変数ならびに事実上のfinal変数(※)の参照が可能

※ コンパイラが、参照が変わらない変数をfinalとしてみなすように変更された。

参照が変わらないなら、外部のListのaddなど実行できるが、行うべきでない。(後述)

thisの扱い

– ラムダ式中のthisは、ラムダ式を定義したメソッドのインスタンスのthis

ラムダ式でthisを使う事は余り無いが、一応注意。

標準Functionは合成可能

– Functionなど、標準関数インターフェースはandThenなど、関数合成に関するメソッドをdefaultメソッドとして定義している。

Page 21: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 20

コレクションに追加されたメソッド (example.CollectionLambda.java)

コレクションにもラムダ式を取るメソッドが追加されています。

- Iterable#forEach(Consumer)

– List#sort(Comparator)

– Map#forEach(BiConsumer)

– Map#compute(key, Function)

– これらのメソッドは、後述するデフォルトメソッドにてインターフェースに実装がある。

// Listの中身を出力Arrays.asList(1,2,3,4).forEach(System.out::println);// 演習1 ソートの別解List<Emp> list = TestData.allEmployees();list.sort(Comparator.comparing(Emp::getName));list.forEach(e -> System.out.println(e.getId() + ":" + e.getName()));// Mapの値更新、出力Map<Integer, Integer> map = new HashMap<>();map.put(1, 2);map.put(2, 3);// キーが2の値を累乗する。map.compute(2, (k, v) -> v * v);map.forEach(

(k,v)-> System.out.println(String.format("key=%d:value=%d", k,v)));

Page 22: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 21

Stream API

Page 23: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 22

Stream APIの概要

仕様

– JEP107 Bulk Data Operations for Collections

– IOストリーム とかあるのに、Stream APIとか言っちゃうんだ、、、

できるようになったこと

– コレクション等一連の要素の集まりに対して,ラムダ式を使用した抽出/演算/変換等の一括操作の提供、および、逐次処理・並列処理(※)の提供

影響

– コレクションに対する操作をfor/whileを使った方式から、Stream APIを使用した新しい方式で書けるようになる。

–利点

宣言的であり、読みやすく、間違いが少ない

並列処理への切り替えが容易

配列・リスト・入出力などが同じ方法で扱える

–欠点

Stream APIを使いこなすには覚える事が多い

実用するには色々と機能が足りてない

Page 24: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 23

例題

例題

–文字列のリストから整数とみなせる文字列を抽出し、整数に変換し、正の整数のみを抽出し、3倍する。

Page 25: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 24

従来の方法(example.StreamBasic.java)

例題

–整数リストから偶数の要素のみ抽出し、数を2倍した要素を取得する。

– このような手続き型のコードは、条件分岐がネストしだすと、コードをよく見ないと、要件に合致する処理が書いてあるかわからない。

List<String> list = Arrays.asList("A001", "100", "-200", "ABC", "92");

List<Integer> res = new ArrayList<>();for (String s : list) {

if (s.matches("[-+]?\\d+")) {int i = Integer.parseInt(s);if (i > 0) {

res.add(i * 3);}

}}System.out.println(res);//300, 276

Page 26: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 25

Stream API による解法(example.StreamBasic.java)

例題

–整数リストから偶数の要素のみ抽出し、数を2倍した要素を取得する。

解説

– Streamという一連の要素の集まりに、抽出(filter)・演算(map)のような操作を連結して繰り返し処理の内容を組み立てる。最終的にcollectメソッドで、操作した要素をList等に変換する。よくわからなければ、SQLでイメージしてもらうといいかもしれません。

– 操作の内容はラムダ式で何をするのかを書く。

– なんとなく、例題の処理内容とラムダ式の内容が揃ってる感じがしませんか。

– あと変数も少ないですよね。

List<String> list = Arrays.asList("A001", "100", "-200", "ABC", "92");

List<Integer> res2 = list.stream() // Streamを‚生成.filter(s -> s.matches(“[-+]?\\d+”))//整数のみ抽出.map(s -> Integer.parseInt(s)) //文字列→整数.filter(i -> i > 0) // 正の整数のみ抽出.map(i -> i * 3) // 3倍する.collect(Collectors.toList()); //結果をListとして取得

System.out.println(res2);

Page 27: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 26

Stream APIとは

大雑把にいうと

– ものすごいイテレーター。

繰り返し処理に、条件抽出や変換、件数指定などの操作を幾らでも設定できる。

–繰り返し処理の方法はStream APIの中に隠蔽される。

今まで繰り返しは、for/while/拡張for文などを選んでいたが、その必要がない。

– 繰り返し処理の最適化をStreamに任す。

プログラマはStreamにどのような操作を行うか中心に書く。(宣言的なプログラム)

性質

– コレクションとは異なるクラス

生成 - Streamを使うには既存のコレクションや配列から、別途変換が必要

– Stream の各種メソッドは大別して以下の2種類に分かれる。

中間操作 – 生成で構築した集合に対する演算を適用する。

終端操作 – ストリームの集合から、コレクション、配列等への変換を行う。

–逐次処理/並列処理

繰り返し処理の方法を簡単に並列処理にできる。(処理の仕方を、プログラマが書く必要がないため。)

–遅延処理

繰り返し処理は最後に一度だけ実行される。

Page 28: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 27

生成・中間操作・終端操作

生成で様々なオブジェクトからStreamを作る

中間操作でStreamに設定を追加する

終端操作でStreamを処理して結果を得る

–繰り返し処理や並列処理の詳細はStreamの中だけで完結する

List

配列

IO

生成 終端操作Stream

中間操作 List

int

Etc..

Page 29: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 28

Stream の生成

生成

–色々なデータからStreamを生成するメソッドが、多数追加された。

コレクションから – Collection#stream, parallelStreamメソッド

配列 - Arrays#stream

IOストリーム、ファイル – BufferedReader#lines, Files#lines

任意の可変長引数で – Stream#of

文字列から文字のストリーム – String#codePoint

無限数列 – Stream#iterate, Stream#repeat

範囲生成(1から100までとか) – IntStream#range

他多数

–ストリームを構築するコードを書く必要はありますが、逆に、元がコレクション、配列、ファイル、その他、何であっても一旦ストリームにしてしまえば、後は同じ方法で扱う事ができる。

Page 30: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 29

Stream の中間操作

中間操作(抜粋)

– Streamの要素に対する何らかの演算の指定を行うメソッド。中間操作は必ずStreamが戻り値なので、メソッドチェーンで連結できる。

filter – 条件ラムダ式に一致する要素のみを抜き出す。

map – 要素をラムダ式に適用して計算・変換する。

flatMap - 要素をStreamを生成するラムダ式に適用し、要素の増減を行う。

parallel – 繰り返し処理を並列処理で行う指示をする。

limit – 繰り返し処理を行う件数を制限する。

skip - 繰り返し処理を件数分スキップする。

以下は状態がある中間操作と呼ばれ、全ての要素の評価が終わらないと実行できない。そのため、性能に影響を与える可能性がある。

– sort - 要素を並べ替える。

– distinct – 要素の重複を除外する。

Page 31: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 30

Stream の終端操作

終端操作(抜粋)

– Streamから結果を生成する操作。終端操作を行った時点でStreamに設定された中間操作が順次実行され、終端操作の内容で結果が作られる。1つのStreamには、1回だけ終端操作を行うことができる。

anyMatch – ラムダ式の条件に合致する要素があるかbooleanで返す。※

allMatch – 全ての要素がラムダ式の条件に合致するかbooleanで返す。 ※

max, min – Comparatorを渡し、要素の最大・最小値を返す。

reduce – 全ての要素の合計値を出すなど、1つにまとめた結果を返す。

forEach - コンソール出力等、任意の副作用を実行する。

count – 件数取得

findFirst – 最初の1件を返す ※

collect – 汎用的な集積操作。Collectorsユーティリティによく使う操作が定義済み。

– Collectors#toList – 要素をListへ変換する。

– Collectors#joining – 要素を1つの文字列にする。接続句を指定可能。

– Collectors#partitionBy – 要素をラムダ式の条件を満たすものと満たさないものに分割する。

– Collectors#groupingBy – 要素をラムダ式の条件にしたがって複数のグループに分割する。

–※ anyMath, allMath,findFirstなどはショートサーキット評価であり、条件が一致した時点で要素を全て評価せず処理を打ち切る。

Page 32: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 31

Stream コードサンプル (example.StreamSample.java)

今までこういう処理って、似たようなforループを何度も書いていましたよね。

–負数があるか?

–全てaから始まるか?

– ファイルの101行目から200行目だけを出す。

– -文字列の間に”,”をつけて1つの文字列に。

boolean negative = Stream.of(1,2,23,-4,5,-6).anyMatch(n -> n < 0); // true

boolean startsWith_a = Stream.of("angel", "apple", "banana").allMatch(s -> s.startsWith("a"));//false

Files.lines(new File("log.txt").toPath()).skip(100).limit(100).forEach(System.out::println);

String[] sa = new String[]{"AA", "BB", "CC", "DD"};String concat = Arrays.stream(sa)

.collect(Collectors.joining(","));

String concat2 = String.join(“,”, sa);//別解

Page 33: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 32

Stream コードサンプル2

–整数リストの乗算 (Java8 ではreduceよりcollectを推奨)

–単語リストを始まりの1文字でグループ化したい。

グループ化した要素はデフォルトはリストだが、任意の変換を適用する事も可能

–ユーザーの平均年齢を得たい。

String[] word={"apple", "angel", "banana", "bush", "cross", "dart"};Map<Character, List<String>> group = Arrays.stream(word)

.collect(Collectors.groupingBy(w -> w.charAt(0)));

int sum = Stream.of(1,2,3,4,5).reduce(1, (a, b) -> a * b);//120

double avg = userList.stream().collect(Collectors.averagingDouble(u -> u.getAge()));

1 2 3 4 5

1 1 2 6 24 120

整数リストの要素(b)

集積器 (a)

Reduce(折りたたみ演算)のイメージ

* * * * *

Page 34: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 33

Stream コードサンプル3 (example.RangeSample)

–範囲生成

IntStreamを用いて、1から10のような数の範囲を作成できる。

–無限ストリーム

Stream#iterateは初期値からラムダ式を適用し、無限に値を生成する。

– そのままforEachなどとすると無限ループとなるが、有限のストリームと組み合わせる(zip)、件数制限を行う(limit等)によって、breakに相当する仕組みを作れる。

// 1から10の合計int sum = IntStream.range(1, 11).sum();

// 0から9の数字のうち、乗算が25を超える数の組み合わせを求める。List<String> comb = IntStream.range(0, 10).boxed()

.flatMap(x -> IntStream.range(x, 10).boxed().filter(y -> x * y >= 25).map(y -> x + "," + y))

.collect(Collectors.toList());

// 無限数を用いて、自然数のうち3または5で割れる100番目の数を求める。Optional<Integer> num = Stream.iterate(1, i -> i + 1) //無限ループ?

.filter(i -> i % 3 == 0 || i % 5 == 0)

.skip(99).findFirst();

num.ifPresent(System.out::println);//215

Page 35: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 34

演習2(20分)

オブジェクトを対象として、Stream APIの各種操作を行います。

パッケージ: practice2

– LetsStream

ポイント

– filter, map, collect(Collectors.toList())が基本。

–包含判定は、anyMatch,allMatch,noneMatchが使える。

– collect,CollectorsにはtoList以外に色々便利機能がある。

joinging – 文字列集合に接続句(オプションで接頭句、接尾句)もつけて1つの文字列に。

Sepalateby – 集合を2つに分ける。分け方はbooleanを返すラムダ式で指定

groupingBy - ↑を一般化。集合をラムダ式の演算結果によって分ける。

avaraging, sumarying, summing – 平均、サマリ、合計の算出。

– flatMapが最初はわかりにくいと思います。これは2重ループに相当すると思ってください。(TestData.javaのallSection()とflatMapの演習を比較してみてください)

Page 36: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 35

休憩

Page 37: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 36

並列処理への対応

繰り返し処理を並列で行うには?

– forループをマルチスレッドにするには多大な労力が伴う。

– Stream APIでは、parallelメソッドを呼ぶだけ

example.TryParalle.java

–並列処理でも要素の順番は保証される。(一部例外あり)

分割して処理した要素を、1つにまとめ、元の順番に復元する。

逐次並列・並列どちらで行っても同じ結果

ただし、以下の場合は例外

– forEachメソッド (順序が必要ならforEachOrderedを使う)

– 中間操作 unordered(順序保証をなくす)を実行した場合。

List<Integer> list = IntStream.range(1, 1000).boxed()

.parallel().peek(i -> System.out.println(“N=" + i)).map(i -> i * 100 + i).collect(Collectors.toList());

System.out.println(list);

Page 38: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 37

並列処理への対応2

–一部の中間操作や終端操作と併用する場合、非効率となる事があるので、注意が必要。更に、場合によっては無限ループになることがある。

skipは先頭N件の読み飛ばす、findFirstは先頭1件を取り出す、このような処理は並列処理の場合全ての要素の評価が終わって順序付けしないとどれが最初のN件だったかがわからない

– 逐次処理だとforループの読み飛ばしなどで簡単にできるが、並列だと効率が悪くなってしまう例

– 要素が無限の場合は評価が終わらないので、無限ループになってしまう!

並列をやめるか、unorderedを指定する。

–外部変数の状態に依存するラムダ式を書くと不正な結果になる。

forEachで、外部の変数に要素を追加するような処理は並列処理で順番がずれる。

– 概ね、そのような処理は終端操作で実現できる。

–並列化のコストがかかるので、何でも並列にすれば良いわけではない。

効果が大きいのは以下が両立する場合

– 繰り返し処理1回あたりの処理時間が長い

– 要素数が多い

Page 39: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 38

遅延処理

問題 ファイルlog.txtがオープンするのはどこ?

– ファイル”log.txt”の”error”が含まれる行に”Error = “を付けて10件まで表示する。

Files.lines(new File("log.txt").toPath()).filter(line -> line.contains("error")).map(line -> "Error = " + line).limit(10).forEach(line -> System.out.println(line));

Page 40: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 39

遅延処理 2

答 – 終端操作(forEach)が実行されるとき。

–中間操作は操作の予約をするだけ。

–終端操作で、要素ごとに中間操作を適用していく。

以下のような処理が実行される。

繰り返し処理は1度だけ行われる。(中間操作ごとに繰り返し処理を行ったりしない)

そのため、無限のストリームも問題なく扱える。

try(BufferedReader br = Files.newBufferedReader(new File("log.txt").toPath())) {

String line;int i = 0;while((line = br.readLine()) != null) {

if (i == 10) break;if (line.contains("error")) {

String line2 = "Error= " + line;System.out.println(line2);

}i++;

}}

Page 41: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 40

Stream APIと上手く付き合うコツ

ラムダ式は複雑にしない。

– filter, map等の操作を重ねる事で、ラムダ式自体は単純にする。

テストが簡単になる。操作の順番の入れ替えが容易。

– Filterは要素の判定、 mapは変換、forEachは順次処理という様に、決まった役割があるので、それ以外の処理をラムダ式で行わないようにする。

以下は、filteで行うべき処理がforEachに全て書かれてしまっている悪い例。

外部変数は参照だけする。決して状態を変更しない。

– 参照の変わらない変数であれば、外部変数のメソッドは実行できるが、並列実行すると順序が変わるので行うべきでない。

Stream の処理結果を終端操作の戻り値以外の手段で取得するのは、悪手。

– 外部状態に関わらず、同じ引数なら同じ結果を常に返す関数を、javaではステートレス操作と呼ぶ(純粋関数とも)。可能な限りステートレスなラムダ式を作成すること。

Stream.of(1,2,3,4).forEach(i -> {if (i % 2 == 0) {

System.out.println(i);}});

List<Integer> list = new ArrayList<>();Stream.of(1,2,3,4,5).map(i -> i * 2).parallel()

.forEach(i -> list.add(i)); //listの順番がめちゃくちゃList<Integer> list2 = Stream.of(1,2,3,4,5).map(i -> i * 2).parallel()

.collect(Collectors.toList()); //OK

Page 42: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 41

Streamがいけてるところ

リストの詰め替えや、条件判定なんかはStreamの方が簡単に書けるし、どんどん使っていい。

– 変数の状態を更新する処理が減るので、バグが少なくなる(かも)

中間操作の順序を変えることで、処理内容を変えることが簡単にできる。

普通のforループの方が性能が良いんじゃないかと思うかもしれません。多分事実ですが、そういうことは性能的な問題が出てから考えましょう。

– forループとStreamの処理速度の違いは感じない。

– コード1箇所での最適化なんで、全体から見れば微々たる物。

– 最悪、並列処理にするという選択肢も取れる。

なおStreamのラムダ式を短く書くには以下を工夫すると良い。

– 引数ありのコンストラクタやオブジェクト生成のstaticメソッドを作る。

mapでオブジェクト生成を1行で書ける。

– equals,hashCode,toStringのオーバーライドや、判定メソッドなどの追加。

groupingByなどの集約条件や、filterの条件などが短く書ける。

Page 43: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 42

Streamがいけてないところ (私見)

正直何度も .stream(), .colletct(Collectros.,,,,) を書くのは飽きる。

–下位互換と並列処理の境界を設ける為にしょうがないとは思うけど、anyMatchとか、groupingByとかぐらいは、Listにあってもいいじゃんとは思う。(そんな事言ったら、コレクションと配列に同じメソッド作らんといけないし無理なんだろうけど)

–ついでに言うと、ラムダ式の変数名、変数宣言が必須なのもどうにかなりませんか。

map(i -> i * 3) を、 map(i * 3) と書きたい!

– 今の仕様なら、メソッド作ってメソッド参照にするしかない、、、、scalaなら。。。

StreamのJavadocを読むにはかなりの修行がいる。

–関数型インタフェースの種類と型の対応が理解しないと、どんなラムダ式が書けるか解らない。

ex) BiFunction<T,U,R> は、 (T, U) -> R というラムダ式。

–説明に当然のように出てくるメソッド参照。

– Stream全般もそうだが、Stream#collectの仕組みも複雑。

–ついでに言うと、コンパイルエラーのメッセージもひどい。

終端操作で結果をファイル出力するには、どうするのが正解なのか。

BufferedReader#lines,Files#linesから作ったStreamはcloseしないとリソースが解放されない(try-with-resource句も使える)。

Page 44: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 43

Streamがいけてないところ (私見)2

デバッグは困難。

–ブレークポイントと相性悪い。

中間操作にブレークポイント入れても、繰り返し処理中に止まるわけではない。

途中の結果を見るのが難しい。

–対策

Peekを上手く使う(デバッグ用途で使うなら、toStringを定義してあるとシンプルになるので良い)

複雑なラムダ式を書くより、1行のラムダ式で中間操作を重ねるようにしよう。

ユニットテスト

他の言語のStream API相当の機能には、zipと呼ばれる機能があるが、Stream APIにはなく、自作する必要がある。

– インデックスアクセスとか、前後の要素をみたい というような処理を行うにはzipが必要。

– zipを使うとなると、タプルのようなオブジェクトを組み合わせにできるものがリテラルとして欲しくなる。

– zipの概要はこちらをどうぞ。

– http://enterprisegeeks.hatenablog.com/entry/2014/05/19/100422

Page 45: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 44

演習3(20分)

Zipを使ってみよう

パッケージ:practice3

– ソース :ZipPractice.java

Zipとは?

– 2つの集合を組み合わせ、要素同士のペアを作る操作。

– zipによって既存のforループで行う様々な処理を置き換え可能になる。

– Stream API相当の機能を持つ他の言語には大抵あるが、javaには無い。

途中までありましたが、リリース時に消えました。。。

clojureにもrubyにもC#にもscalaにもあるのに。。。。

1 2 3 4 5

a b c d

zip 1,a 2,b 3,c 4,d

要素の数が合わない場合は短い方に合わせる。

Page 46: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 45

演習3-補足

前後の要素比較

–通常のリストと、1つ要素をずらしたリストを用意することで、前後の要素が比較可能となる。

1 2 3 4 5

2 3 4 5

zip 1,2 2,3 3,4 4,5

1

Stream#skip(1)

Page 47: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 46

インターフェース拡張

Page 48: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 47

インターフェース拡張

仕様

– JEP126 Lambda Expressions & Virtual Extension Methods

できるようになったこと

– インターフェースに、staticメソッドが定義可能になった。

– インターフェースに、実装が持てるようになった。(デフォルトメソッド)

部分的な多重継承の実現

影響

– Java8では、ラムダ式やStream APIが注目されているが、デフォルトメソッドの方がクラス設計に与える影響は大きい。

–デザインパターン、フレームワークも再考されるのでは。

Page 49: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 48

インターフェース static メソッド

概要

– インターフェースにstaticメソッドを書く事ができる。(そのまんま)

– 呼び出す場合は、 インタフェース.メソッド名 で良い。

– staticメソッドがあるインターフェースを継承したインターフェースや実装したクラス名で、インターフェースのstaticメソッドを呼ぶ事は当然できない。

– mainメソッドも書けるし、実行できるよ!(だからどうした)

使いどころ

– 正直あまり思い浮かばない。ユーティリティメソッドの置き場とか?

Java APIでの使用箇所

– java.util.Comparator – ラムダ式を使ったComparator作成など色々増えた。

コンパレータの生成が簡単にできるものがある。

public interface TestIF {static void print(){System.out.println("test");}static void main(String[] args){TestIF.print();}//testと表示

}

Page 50: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 49

デフォルトメソッド

概要

– インターフェースに内容有りのメソッドを定義可能。(default キーワード使用)

– 実装したクラスは、デフォルトメソッドを実装しなくてもよい。(オーバーライド可能)

– インターフェースなので複数実装可能。限定的な多重継承の実現。

メソッドが被ったら? →コンパイルエラー。オーバーライドして解決する。

– 抽象クラスとは異なる点は以下の2つ。

1. 抽象クラスは継承できるのは1つ。インターフェースは複数実装可能。

2. 抽象クラスはフィールドを持てる。インターフェースは持てない。(thisを上手く使おう)

なぜ導入されたか?

– ラムダ式、Stream APIの導入で、List等既存のAPIに相当数のメソッド追加があり、実装クラスのメソッド追加が半端なかったため。デフォルトメソッドでインターフェースにメソッドを追加しても、実装クラスのソースを修正する必要がない。

使いどころ

– 色々ありそう。ログ出力とか。

– AOPとかDIとか、今までリフレクション、アノテーション、設定ファイル、黒魔術な手段で解消してきた事が簡単になりそうな気配はある。

– が、これだ!というのは思いつかないですね。実例が揃うのを待ちたい。

Page 51: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 50

デフォルトメソッド – 使用例1

単純な例

interface ConsoleLogger {default void log(String msg) {

System.out.println("message = " + msg);}

}public class DefaultTest implements ConsoleLogger {

// ConsoleLoggerのlogメソッドは上書き不要public void test() {

log("user");}

public static void main(String[] args) {new DefaultTest().test();// message = user

} }

Page 52: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 51

デフォルトメソッド – 使用例2

デフォルトメソッドが重複する例

interface Bar {default void print(){System.out.println("Bar");}default void p(){System.out.println("p");}

}

interface Foo {default void print(){System.out.println("Foo");}

}// printが衝突public class DefaultTest2 implements Bar,Foo {

@Override public void print() {// インターフェース.super.メソッドで参照可能Bar.super.print();

}}

Page 53: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 52

デフォルトメソッド – 使用例3-1(紹介のみ)

実践的な例

– 参考 : http://yojik.hatenablog.jp/entry/2013/11/02/172132

– 実装するインターフェースを変えるだけで、エラーログの出力をコンソール/ファイルに切り替える。

Page 54: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 53

デフォルトメソッド – 使用例3-2

(続き) 具象クラスの継承するときにデフォルトメソッド有りのインターフェースを選択する。具象クラスの実装は不要。– 欲を言うと、匿名クラス作成時にインターフェースも指定できると、具象クラスの宣言も不要でいいんだけど、、、例) Process p = new Process implemets InfoConsoleLog, ErrorConsoleLog(){};

実装はこちら: – https://gist.github.com/kencharos/1518df9e660e2873b5fb

// エラーログをコンソールに出す場合。class ProcessImpl extends Process

implements InfoConsoleLog, ErrorConsoleLog{}// こう定義するとエラーログはファイルに出る。class ProcessImpl extends Process

implements InfoConsoleLog, ErrorFileLog{}

public class DefaultExample {public static void main(String[] args) {

Process p = new ProcessImple();p.go();

}}

Page 55: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 54

Optional

Page 56: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 55

Optional

Optional型って?

–失敗するかもしれない計算(nullを返すかもしれないメソッド)の結果を表す。

– nullを扱えないHaskellで nullのような概念を表す型、Maybe a(Nothing | Just a) から由来。

– Stream APIのmaxなど、Streamの一部のメソッドはOptionalを返す。(要素が0個のStreamから最大値なんて探せないから)

利点

– メソッドの戻り値が例えば、Stringだと、そのメソッドがnullを返すかどうかは、メソッド宣言からだけでは一切解らない。

– もし戻り値が、 Optional <String> なら、 メソッドはStringの結果を返すかもしれないが、返さないかもしれないということが読み取れる。また、同時にOptionalから結果があるかを判定するコードを書かざるを得ないので、NullPointerExceptionの発生を抑制する。

– というわけで、nullを返すようなメソッドを作るときには、代わりにOptionalを使う事を検討しても良いかもしれません。

DAOのfindByPkとか。

Page 57: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 56

サンプル

Optional型のオブジェクトから、計算結果を取得する各種サンプル。他にも色々なメソッドがあるので、javadocを参照。

// Optionalを返すメソッドを実行Optional<String> res = someMethod();// isPresentは結果があるか判定。getは値がある場合のみ成功する。if(res.isPresent()) {

System.out.println(res.get());}

// 値がある場合のみラムダ式を実行する。(上と同じ動作をする)res.ifPresent(r -> System.out.println(r));

// 値があれば取得し、なければ引数の値を使う。String s = res.orElse("nothing");

Page 58: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 57

演習4(25分)

デフォルトメソッドを使って、Mapにキーから取得した値をOptionalでラップした値を返す機能を追加します。

パッケージ : practice4

– OptionalMap(Mapを継承)

インターフェース。デフォルトメソッドgetByOptの中身を記述してください。

– キーに紐づく値が無い場合空のOptional、そうでない場合値をOptionalに包んで返す。

– OptionalHashMap

HashMapを継承し、OptionalMapを実装します。

継承クラスをHashMapからLinkedHashMapに変更する事も容易だということが解ると思います。

– Test

OptionalHashMapを使用したテストです。

Page 59: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 58

OptionalにもあるflatMap

Optionalには、Stream同様、 map, flatMapメソッドがあり、Optionalの中の結果に対して、演算が適用できる。

– さらに、Optionalに結果がない場合は何もしないという性質がある。

– この性質を生かすと、flatMapでは段階的な取得のような処理が簡単に書ける。

例) Mapから取得した値をキーとしてMapから再度値を取得を3回行う。

– flatMapは”モナド”と言う計算パターンの一種。

//mapからの取得値をOptionalに包むprivate static <T> Optional<T> get(Map<T,T> map, T key) {

return Optional.ofNullable(map.get(key));}

// “A”を起点にmapから3度段階的に取得を行う。// どの時点でmapから取得できなくなっても、エラーにならない。get(map, "A")

.flatMap(k -> get(map, k)) //kはmapから”A”で取得した値。

.flatMap(k -> get(map, k))

.ifPresent(System.out::println);//値があれば表示。

Page 60: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 59

その他トピック

Page 61: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 60

新日付API(java.time) -example.TimeTest

java.timeパッケージ(JEP150 Date&Time API)の主なクラスは以下の通り。

– 日付・時間を表す - LocalDate(日付), LocalTime(時間), LocalDateTime/ZonedDataTime(日時)

– 時間の量を表す - Duration(時間の量), Period(日付の量)

– 日付・時間の計算メソッドに上記の量を渡して計算を行う。

Date,Calendarよりも簡単・高機能。あとスレッドセーフ。

– 1つの日時オブジェクトで時間の表現、時間計算、文字列変換ができる。(Data/Calendar間の変換不要)

– 日時オブジェクトを様々な方法で作成できる。

– 月が1オリジン(1が1月)である。(Calendarは0が1月)

– 和暦のようなロケールに応じた年代を表示できる。

– 日時オブジェクトは不変。計算結果は別オブジェクトになる。

Date/Calendarの置き換えを狙っているため、Date/Calendarとの変換メソッドは提供されない。

– 新APIを使うのなら、Date/Calendarを一切使うなという意思表示。保守案件には無用の代物かもしれない。

その割には、JDBCでは新APIにまた対応していない。– 普及には時間がかかりそう。

LocalDateTime may = LocalDateTime.of(2014, 5, 8, 13, 0, 0);

LocalDateTime calcDate = may.minusMonths(1).plus(Period.ofYears(2));

System.out.println(calcDate.format(DateTimeFormatter.ofPattern("yyyy/MM/dd hh:mm:ss")));

Chronology jp = Chronology.of("Japanese");

ChronoLocalDate wareki = jp.date(may);

System.out.printf(

"%d年は%s %d年\n",wareki.get(ChronoField.YEAR), wareki.getEra(), wareki.get(ChronoField.YEAR_OF_ERA));

使用例 年,月,日,時間,分,秒を指定して日時を生成

日付の計算は新しい日時を生成する

2014年はHeisei 26年 と出力和暦の出力例

Page 62: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 61

JVMまわり

Java8には、JVMや周辺ツールの変更もある。

例えば

– Sun JVM(Hotspot)と Oracle JVM(jRockit)の統合

–パーマネント領域の廃止(-XX:PermSizeオプションが無効)

JVMオプションが色々と変更

– Javascirptエンジンの変更 (Rhino → Nashorn)

– APTの廃止

– Javadocツールの強化(コメントチェック、構文木生成)

Java8へのアップデート案件では、プログラムの稼動確認だけでなく、JVMの起動パラメータやパフォーマンスなんかも確認しておきましょう。

他にも

– javacコマンド のバージョン指定(-source, -target)は3世代前までのみをサポートするようになります。

–現在のJDK8でもjava1.5以前向けのコンパイルは可能ですが、将来削除されるという警告が発生します。

Page 63: Java8勉強会

ULSCopyright © 2011-2014 UL Systems, Inc. All rights reserved.

Proprietary & Confidential Powered by 62

まとめ

Java8は、Java5のジェネリクス追加に匹敵するアップデート

インターフェース実装、ラムダ式、Stream API 全て役に立ちます。

–使いこなせる必要は無いですが、これらの要素はAPIのいたる所に出てくるので、概要や使い方くらいは知っておくべき。

–詳しく知るならJavadocを読むか、enterprisegeeksで。

是非、「何だかわからないからラムダ禁止」みたいな風潮と戦いましょう。

Java8採用にむけて訴求できそうなポイント

– Stream APIで並列処理による高速化が容易になる。

– Stream APIを使って、省コードかつバグリにくく保守性が高いプログラムが作れる

– Java7 のEOLは2015年4月