Java SE 9の紹介:
モジュール・システムを中心に
2017-11-18 JJUG CCC 2017 Fall
ハッシュタグ: #ccc_e4 #jjug_ccc
宮川 拓
 @miyakawa_taku
 JJUG幹事です
 株式会社JSOLで働いてます
 尾上部屋の里山さんのファンです
 オレオレJVM言語Kinkを作っています
https://bitbucket.org/kink/kink
自己紹介 #ccc_e4
2/104
#ccc_e4本セッションのあらまし
 9月にJava SE 9がリリースされました
 新しく導入されたモジュール・システムを
解説します
 その他のアップデートも概観します
3/104
演目
そもそも
Java SEってなんだっけ?
Java SE 9の
モジュール・システム
Java SE 9の
その他のアップデート
#ccc_e4
4/104
演目
そもそも
Java SEってなんだっけ?
Java SE 9の
モジュール・システム
Java SE 9の
その他のアップデート
#ccc_e4
5/104
#ccc_e4Java SEとは
Java SE: Java処理系の規格
 正式名称: Java Standard Edition
 規格の中身:
 言語仕様
 Java仮想マシンの仕様
 標準APIの仕様
6/104
#ccc_e4ちなみに: Java EEとは
Java EE: APサーバの規格
 正式名称: Java Enterprise Edition
 規格の中身:
 サーブレット
 JMX
 EJB
 ...
7/104
Java SEタイムライン #ccc_e4
ジェネリクス, Concurrency Utilities
ServiceLoader
invokedynamic
ラムダ式, Stream API
モジュール・システム (a.k.a. Jigsaw)
2004.10
Java SE 5
2006.12
Java SE 6
2011.07
Java SE 7
2014.03
Java SE 8
2017.09
Java SE 9
2018.03
Java SE 18.3
var記法?
1 0 年戦える言語に
Functional Java
基盤の大改造
8/104
#ccc_e4Java SE 9はどんなリリース?
Java SE 9:
 プログラムの字面のレベルでは地味
 アプリケーション基盤には影響大
 モジュール・システムの導入!
9/104
演目
そもそも
Java SEってなんだっけ?
Java SE 9の
モジュール・システム
Java SE 9の
その他のアップデート
#ccc_e4
10/104
#ccc_e4モジュール・システム
このセクションの目的:
 モジュール・システム対応に向けて、
心の準備ができること
11/104
#ccc_e4モジュール・システム
内容:
 モジュール・システム導入の背景
 モジュール・システムの基本
 リフレクション
 コンパイルと実行
 モジュール世界への移行
 モジュールのテスト
 高度なトピック
12/104
モジュール・システム
導入の背景
#ccc_e4
13/104
#ccc_e4Java 8までの課題
 JAR地獄
 「内部向けパッケージ」の存在
14/104
#ccc_e4~Java 8: JAR地獄
 JAR地獄 := 大量のJARがクラスパスに
展開されて、混沌にブチ込まれること
 「このJARいらないんじゃない?」
 → 消したら実行時エラー
 Maven, Gradleのような依存性管理
ツールを使えば少しはマシだけど……
15/104
#ccc_e4~Java 8: JAR地獄
複数バージョン混在地獄
ゲーム Ace Shooter
matrix-1.0.0.jar
matrix-impl-2.0.0.jar
matrix-api-2.0.0.jar
physics.jar
名前の異なるMavenアーティファクトが
同じパッケージ/クラスを含む場合があり得る
org.example.matrix.api.*
org.example.matrix.impl.* org.example.matrix.api.*
org.example.matrix.impl.*
😥
16/104
#ccc_e4~Java 8: 内部向けパッケージ
 プラットフォームやライブラリ内で、
「内部向け」パッケージを作ることがある
 org.kink_lang.kink.internal.xxx
 sun.misc (JDK内)
 内部向けパッケージは本来、ライブラリ外
からの使用を想定していない
 が、しかし
17/104
#ccc_e4~Java 8: 内部向けパッケージ
GitHubで“import sun.misc.Unsafe”を検索
18/104
~Java 8: 内部向けパッケージ #ccc_e4
😥
19/104
#ccc_e4~Java 8: 内部向けパッケージ
 内部使用向けのはずのUnsafeクラスが広く
使われてしまっている
 性能を絞り出すため……
 なにが問題か?
 移植性の低いプログラムの蔓延
 既存のプログラムを壊さないように配慮
すると、実装の改善がしづらい
20/104
モジュール・システムの基本
#ccc_e4
21/104
#ccc_e4モジュール・システム
Java SE 9での解決策
 JARを名前付きのモジュールとして、
カプセル化された配置の単位に
 これまでの課題:
 JAR地獄
→ モジュール単位で依存関係を整理
 内部向けパッケージ
→ 外部からの使用を実質的に制限
22/104
#ccc_e4モジュール・システム
各JARファイルが名前付きのモジュールに
ace-shooter.jar
= module net.example.ace_shooter
matrix.jar
= module org.example.matrix
physics.jar
= module jp.example.physics
モジュール名は逆ドメイン名方式が推奨
23/104
パッケージ衝突の検知 #ccc_e4
module net.example.ace_shooter
module org.example.matrix
module
org.example.matrix.impl
module
org.example.matrix.api
module jp.example.physics
複数のモジュールが同一パッケージを含む場合、
起動時に検知される †
org.example.matrix.impl.*
org.example.matrix.api.* org.example.matrix.api.*
org.example.matrix.impl.*
😃
24/104
#ccc_e4module-info.java
モジュール名はソースディレクトリのトップ
レベルのmodule-info.javaで宣言
module org.example.matrix {
}
JARファイルのトップレベルの
module-info.class
コンパイル
25/104
公開パッケージの指定 #ccc_e4
module org.example.matrix {
exports org.example.matrix.api;
}
モジュールは、他のモジュールに対して公開
するパッケージをexports命令で指定する
exportされたパッケージは
モジュールの外部仕様 の一部とみなせる
26/104
使うモジュールの指定 #ccc_e4
module jp.example.physics {
requires org.example.matrix;
}
モジュールは、自分が使うモジュールを、
requires命令で指定する
モジュールが使えるクラスの条件:
 requiresした先のモジュールの
 exportsされたパッケージの
 publicなクラス
→ 該当しないクラスを使うと
コンパイルエラー / 実行時例外
27/104
exports / requires #ccc_e4
module A {
requires B;
}
module B {
exports B.api;
requires C;
}
module C {
exports C.api;
}
B.apiパッケージ
B.implパッケージ
C.apiパッケージ
→ 😃使える
→ 😥使えない
→ 😥使えない
C.apiパッケージ → 😃使える
28/104
exports / requires #ccc_e4
module A {
requires B;
}
module B {
exports B.api;
requires C;
}
module C {
exports C.api;
}
A.xxxパッケージ → 😃使える
自分のモジュール内のパッケージは、非公開でも使える
B.xxxパッケージ
C.xxxパッケージ → 😃使える
→ 😃使える
29/104
exports / requires #ccc_e4
module A {
requires B;
}
module B {
exports B.api;
requires C;
}
requiresしたモジュールが存在しないと
コンパイルエラー / 実行時例外
30/104
#ccc_e4標準モジュール
Java SEの標準ライブラリも、モジュールに
分割されている
 java.baseモジュールは暗黙的にrequiresされる
 それ以外は明示的にrequiresする必要あり
モジュール パッケージ群
java.base java.lang, java.util, java.io, ...
java.sql java.sql, javax.sql, ...
java.desktop java.awt, javax.swing, ...
... ...
31/104
リフレクション
#ccc_e4
32/104
#ccc_e4リフレクション
モジュールのカプセル化はリフレクションに
も適用される
 外部モジュールからアクセスできるのは、
exportsされたパッケージの、公開クラ
スの、公開メンバだけ
 requires関係は不要
 非公開なメンバへのアクセスは、
外部モジュールからは通常不可能
33/104
#ccc_e4リフレクション
困る例: DI先のクラス
package net.example.ace_shooter.engine;
public class GameEngine {
@Inject private PerfMonitor perfMonitor;
...
} public にしたくない
34/104
exportsしたくない
リフレクションで触ってほしいクラスは
必ずしも公開したいわけではない
#ccc_e4リフレクション
必要な緩和策:
 パッケージをexportsしたくない
 メンバをpublicにしたくない
 でもリフレクションでアクセスさせたい
35/104
#ccc_e4opens命令
opens命令でパッケージを指定することで、
外部からリフレクションができるようになる
module net.example.ace_shooter {
opens net.example.ace_shooter.core;
...
}
36/104
#ccc_e4openモジュール
モジュール宣言にopenを修飾すると、
すべてのパッケージがopens指定される
open module net.example.ace_shooter {
...
}
37/104
リフレクション #ccc_e4
リフレクションは
もはや魔法の杖ではない!
38/104
コンパイルと実行
#ccc_e4
39/104
#ccc_e4コンパイルと実行
注意点:
 プログラムのコンパイル方法、実行時の
プログラム配置方法はJava SE実装依存
 本セッションは、Java SEの参照実装で
あるOracle JDKを前提とする
40/104
#ccc_e4
41/104
モジュールパス
モジュールパス:
 モジュールのJARファイルを配置する
パス
 クラスパスとは別!
#ccc_e4コンパイル
javacコマンドの主なオプション
オプション 内容
--module-path ¥
{モ ジ ュ ー ル パス}
依存先モジュールのモジュールパ
ス
--module-source-path ¥
{モ ジ ュ ー ル ソ ー ス パス}
複数モジュールを同時コンパイル
する時、モジュールのソースディ
レクトリの配置先を指定
※あまり使わないはず
--module-version ¥
{バ ー ジ ョン}
モジュールのバージョン
※いまのところ使い道は無い
42/104
実行構成
#ccc_e4実行
初期モジュール(mainを含むモジュール)か
ら推移的に参照されるモジュールだけが、
実行構成に含まれる(基本的に)
module Main
java.base
java.desktop
java.sql
module X
module Y
module Z
43/104
モ ジ ュ ー ル パ ス
Zやjava.sqlはリフレクションでも触れない
#ccc_e4実行
javaコマンドの主なオプション
オプション 内容
--module-path ¥
{モ ジ ュ ー ル パス}
モ ジ ュ ー ル パ ス
--module ¥
{初 期 モ ジ ュ ー ル名}/{メインクラス }
mainメソッドを持つ
ク ラ ス
--module {初期モジュール 名} 初 期 モ ジ ュ ー ル
--add-modules ¥
{モ ジ ュ ー ル名},{モジュール 名},...
初 期 モ ジ ュ ー ル か ら
推 移 的 に 参 照 さ れ な
い モ ジ ュ ー ル を 実 行
構 成 に 追 加
44/104
経過措置
#ccc_e4
45/104
#ccc_e4経過措置
 現時点で、多くのプログラムのJARは、
module-info.classを含んでいない
 モジュールの世界への経過措置として、
二種類の特殊なモジュールが導入された
 無名モジュール
 自動モジュール
46/104
#ccc_e4無名モジュール
 クラスパス上のクラスファイルは、
無名モジュールの中でロードされる
 無名モジュールは:
 モジュール名を持たない
 すべてのモジュールをrequiresする
 すべてのパッケージをexportsする
 Oracle JDKでは、外部モジュールの非公
開メンバへのリフレクションが可能
47/104
#ccc_e4自動モジュール
 モジュールパス上のJARファイルのうち、
module-info.classを持たないものは、
自動モジュールとしてロードされる
 自動モジュールは:
 モジュールパス上の
すべてのモジュールをrequiresする
 すべての無名モジュールをrequiresする
 すべてのパッケージをexportsする
 すべてのパッケージをopensする
48/104
#ccc_e4自動モジュール
自動モジュールのモジュール名の決定方法:
1. JARのMANIFEST.MFファイルが
Automatic-Module-Name属性を持って
いる場合、その値がモジュール名
2. そうでない場合、JARファイル名を元に
モジュール名が作られる
 commons-io.jar
→ commons_ioモジュール
49/104
モジュール世界への移行
#ccc_e4
50/104
#ccc_e4モジュール世界への移行
ソフトウェア種別ごとのモジュール化指針:
種別 モジュール化指針
ライブラリ いますぐモジュール化!
プラグイン 基盤ミドルウェア次第
APサーバ上の
アプリケーション
基盤ミドルウェア次第
※Java EE 8はモジュール非対応
スタンドアロンの
アプリケーション
遅くなりすぎない程度に
モジュール化する場合もしない場合も、
JDK9への移行ガイドは必読
51/104
#ccc_e4ライブラリのモジュール化
ライブラリのモジュール化が喫緊な理由:
 ライブラリを使う側のプログラムが、
requires命令を書けるようにするため
52/104
#ccc_e4ライブラリのモジュール化
JARファイル名ベースの自動モジュールを
requiresしてはいけない!
module org.example.matrix {
requires commons_io;
...
}
😥
駄目パターン:
→ Commons IOに本当のモジュール名が
付いたとき、ライブラリを使う側のプログラムが
不整合になる
※ requires staticは許容範囲
53/104
#ccc_e4ライブラリのモジュール化
モジュール化のフロー
すべての requires先JAR の
モジュール名が決まるまで待つ
Automatic-Module-Nameで
モジュール名を決めて公開
module-info.javaを書いて公開
すべての requires先JAR の
モジュール名が決まっている?
NoYes
54/104
#ccc_e4ライブラリのモジュール化
Java SE 8以前をサポートしたい時:
 Automatic-Module-Name属性を付ける
→ これは簡単
 module-info.javaが書きたい場合、
module-info.javaだけをJava SE 9用に
コンパイルする
55/104
モジュールのテスト
#ccc_e4
56/104
#ccc_e4モジュールのテスト
二種類のテスト
ブ ラ ッ ク ボ ッ ク ス
・テスト
 モ ジ ュ ー ル の 外 か ら テ スト 。
 exportsされたパッケージの、public な
ク ラ ス を 操 作 し て 、 仕 様ど お りに 動 く
ことを確かめる。
 テ ス ト コ ー ド は 、 テ ス ト対 象 と
別 モ ジ ュ ー ル 。
ホ ワ イ ト ボ ッ ク ス
・テスト
 モ ジ ュ ー ル の 内 側 を テ スト 。
 公 開 ・ 非 公 開 に か か わ らず 、 クラ ス ・
メ ソ ッ ド を 操 作 し て 、 意図 し た実 装 に
なっていることを確かめる。
 テ ス ト コ ー ド は 、 テ ス ト対 象 と
同 一 モ ジ ュ ー ル……どうやって?
57/104
#ccc_e4ホワイトボックス・テスト
ホワイトボックス・テストの課題:
 テストコードは、JUnit, TestNG, JMockit
など、テスト用ライブラリを使う
 テスト対象コードは、これらのライブ
ラリをrequiresしていない
 コンパイル済みのモジュールに、テスト
コードのクラスを追加する必要がある
58/104
#ccc_e4ホワイトボックス・テスト
ふたつのやり方:
59/104
正攻法 → こちらを解説
 テスト対象のモジュール定義を、
テストできる構成に一時的に上書き
 javac/javaコマンドにオプションを指定
次善の策
 あきらめてクラスパス上でテスト
#ccc_e4モジュール定義の上書き
JDKのjavac /javaコマンド共通オプション
オプション 内容
--add-reads ¥
{モ ジ ュ ー ル名}={参照先モジュール},...
requires定義を追加
--patch-module ¥
{モ ジ ュ ー ル名}={ファイル,...},...
モ ジ ュ ー ル に ソ ー
ス フ ァ イ ル/クラス
フ ァ イ ル を 追 加
60/104
モジュール定義の上書き #ccc_e4
javac ... ¥
--add-reads app=junit ¥
--patch-module app=ATest.java,BTest.java
java ... ¥
--add-reads app=junit ¥
--patch-module app=ATest.class,BTest.class ¥
--add-modules ALL-MODULE-PATH
コンパイル
実行
モ ジ ュ ー ル パ ス 上 の 全JARを実行構成に含める
61/104
高度なトピック
#ccc_e4
62/104
#ccc_e4SPI
SPIの仕組みはモジュール・システムに統合
された
 サービスを使うモジュールは、
uses命令でSPIを指定
 サービスを提供するモジュールは、
provides命令でサービスの実装を指定
63/104
#ccc_e4requires static
コンパイル時に必要だが、実行時には不要な
モジュールを、requires static命令で指定で
きる
 例: JSR 305の@Nullableアノテーション
64/104
ブートレイヤー
#ccc_e4モジュールレイヤー
モジュールレイヤー: モジュールの名前空間
java.base
java.sql
jp.example.app_container
java.desktop
java.logging
アプリ X のレイヤー アプリ Yのレイヤー
com.example.app_x org.example.app_y
net.example.utils net.example.utils
子
親
子
親
アプリケーションコンテナや、プラグイン機構の
基礎となる仕組み
65/104
#ccc_e4モジュールレイヤー
クラスローダーとモジュールレイヤーの関連
Module ModuleLayer
ClassLoader
Class
Package
Unnamed
Module
Named
Module
続きはウェブで!
http://d.hatena.ne.jp/miyakawa_taku/20171108/1510145001
66/104
#ccc_e4OSGiとの違い
 OSGiはコンテナの仕組みを仕様に含むが、
Javaのモジュール・システムはコンテナの
仕組みを含まない
 Javaのモジュール・システムは、OSGiの
ようなものを作るための基礎と考えられる
67/104
モジュール・システムの
参考資料
#ccc_e4
68/104
#ccc_e4モジュール・システムの参考資料
Bakker, Mak (2017)『Java 9 Modularity』
69/104
演目
そもそも
Java SEってなんだっけ?
Java SE 9の
モジュール・システム
Java SE 9の
その他のアップデート
#ccc_e4
70/104
#ccc_e4その他のアップデート
モジュール・システム以外の主要なアップ
デートを、対応するJEPを参照しながら
見ていく
71/104
#ccc_e4JEP
 JEP: Java SEの参照実装であるJDKの開発
フロー
 JEP 261: Module System
 JEP 213: Milling Project Coin
 ...
 JEP自体は規格の一部ではないが、
Java SEを理解するのに役立つ
 ※ Javaの規格はJSRで決まる
72/104
JEP 269
コレクションのファクトリ
#ccc_e4
73/104
#ccc_e4コレクションのファクトリ
List.of, Set.of, Map.ofメソッドで、不変な
コンテナが作れるようになった☆
74/104
List.of #ccc_e4
List<String> a
= Collections.emptyList();
List<String> b
= Collections.singletonList("foo");
List<String> c
= Collections.unmodifiableList(Arrays.asList("foo", "bar"));
List<String> a = List.of();
List<String> b = List.of("foo");
List<String> c = List.of("foo", "bar");
~Java SE 8
Java SE 9
75/104
Map.of #ccc_e4
Map<String, Integer> m = new HashMap<>();
m.put("双葉山", 69);
m.put("大鵬", 45);
m.put("白鵬", 63);
Map<String, Integer> yokozuna
= Collections.unmodifiableMap(m);
~Java SE 8
Map<String, Integer> yokozuna
= Map.of("双葉山", 69, "大鵬", 45, "白鵬", 63);
Java SE 9
76/104
JEP 213
try-with-resourcesの改善
#ccc_e4
77/104
try-with-resourcesの改善 #ccc_e4
try (BufferedReader br = new BufferedReader(
new InputStreamReader(
new FileInputStream("foo.txt"),
StandardCharsets.UTF_8))) {
...
}
~Java SE 8
Java SE 9
BufferedReader br = new BufferedReader(
new InputStreamReader(
new FileInputStream("foo.txt"),
StandardCharsets.UTF_8));
try (br) {
...
}
78/104
JEP 213
インタフェースのprivateメソッド
#ccc_e4
79/104
#ccc_e4インタフェースのprivateメソッド
インタフェースにprivateメソッドが定義でき
るようになった
defaultメソッドの実装が複雑になったとき、
その一部がprivateメソッドに抽出できる☆
80/104
JEP 213
匿名クラスでダイヤモンド演算子
#ccc_e4
81/104
#ccc_e4匿名クラスでダイヤモンド演算子
匿名クラスの宣言でダイヤモンド演算子が使
えるようになった
Java SE 9
Iterator<Integer> inc = new Iterator<>() {
private int n = 0;
@Override public boolean hasNext() {
return true;
}
@Override public Integer next() {
return this.n ++;
}
};
82/104
JEP 213
「_」1文字識別子の禁止
#ccc_e4
83/104
#ccc_e4「_」1文字識別子の禁止
「_」1文字の識別子が使えなくなった
 ×変数名
 ×フィールド名
 ×メソッド名
 ×クラス名
 ×ラムダ式のパラメータ
(これは8から)
84/104
JEP 226
ResourceBundleプロパティが
デフォルトUTF-8に☆
#ccc_e4
85/104
ResourceBundle→UTF-8 #ccc_e4
~Java SE 8
Java SE 9
 ResourceBundleのプロパティファイルは
デフォルトでLatin-1
 日本語などのメッセージはnative2asciiで
エスケープする必要があった
 ResourceBundleのプロパティファイルが
デフォルトでUTF-8に☆
 注意: Propertiesクラスのデフォルトは
Latin-1のまま
86/104
JEP 266
Reactive Streamsサポート
#ccc_e4
87/104
#ccc_e4Reactive Streamsサポート
分散システムのプログラミングモデルである
Reactive Streamsをサポート
下記のインターフェースを提供
 Flow.Publisher
 Flow.Subscriber
 Flow.Subscription
 Flow.Processor
88/104
JEP 266
CompletableFutureの強化
#ccc_e4
89/104
#ccc_e4CompletableFutureの強化
 CompletableFutureクラス:
 非同期に完了/キャンセルできるタスク
 Promiseパターンを実現する仕組み
 Java SE 9で便利メソッドがもろもろ追加
 orTimeout: タイムアウト時間を指定
 Executorのファクトリ群
 などなど
90/104
JEP 102
プロセスAPIの強化
#ccc_e4
91/104
#ccc_e4プロセスAPIの強化
 ProcessHandleクラス:
 ネイティブプロセスを表す
 PIDや実行ユーザなどが取れる
 Process.onExit()メソッド:
 プロセス終了時に完了する
CompletableFutureを戻す
 複数の子プロセスからどれか1つの終了
を待ち受ける、のような操作が可能に
92/104
自分自身のPIDを出力 #ccc_e4
Java SE 9
ProcessHandle ph = ProcessHandle.current();
long pid = ph.pid();
System.out.println(pid);
// → 12345
93/104
JEP 277
@Deprecatedの強化
#ccc_e4
94/104
#ccc_e4@Deprecatedの強化
@Deprecatedアノテーションへの属性追加
 since: いつから非推奨になったか
 forRemoval: 将来削除される予定か
Java SE 9
@Deprecated(
since = "3.0.0", // 3.0.0から非推奨
forRemoval = ture // 未来のバージョンでは削除予定
)
public class FatOldSun {
...
}
95/104
JEP 280
文字列結合のindy化
#ccc_e4
96/104
#ccc_e4文字列結合のindy化
文字列結合演算の実装を処理系が選択できる
ように、invokedynamic命令のためのブート
ストラップメソッドが定義された
注意!
 プログラマにとっての指針は変わらない
→ 結合を繰り返す時はStringBuilderを使う
 この変更で得られるのは、極めて微視的で、
処理系依存な高速化の可能性にすぎない
 推測するな、(マクロに)計測せよ
97/104
JEP 219, 244, 249, 273, 229
セキュリティの改善
#ccc_e4
98/104
#ccc_e4セキュリティの改善
 JEP 219: DTLS
 JEP 244: ALPN
 JEP 249: OCSP Stapling for TLS
 JEP 273:
 SecureRandomのアルゴリズム追加
 パラメータ設定用API追加
 JEP 229:
 PKCS2をキーストアファイルのデフォルトに
99/104
JEP 238
マルチリリースJARファイル
#ccc_e4
100/104
#ccc_e4マルチリリースJARファイル
 特定のJava SEバージョン用に、クラス
ファイル/リソースファイルを上書き
 上書き用のファイルの配置場所:
 META-INF/versions /<バージョン>/
 Java SE 8以前と9を両方サポートするため
の、最後の手段として使える
101/104
演目
Java SE 9の
その他のアップデート
Java SE 9の
モジュール・システム
そもそも
Java SEってなんだっけ?
#ccc_e4
102/104
註釈
#ccc_e4
103/104
#ccc_e4† パッケージの衝突検知
「複数のモジュールが同一パッケージを含む場
合、起動時に検知される」
 厳密には、「モジュールレイヤーが、モ
ジュール間のパッケージ重複を許さない構
成である時、そのモジュールレイヤー内の
複数のモジュールが~」である
 異なるモジュールレイヤー間では、同じ名
前のパッケージが共存できる
 defineModulesWithManyLoadersで作られた
モジュールレイヤーは、exportされない
パッケージの重複を許す
104/104

Java SE 9の紹介: モジュール・システムを中心に