モジュール(requires transitive)難易度 標準無料
3つのモジュールが次の module-info.java を持つ。
1 // ===== モジュール com.foo.util =====
2 module com.foo.util {
3 exports com.foo.util; // public class Token を含む
4 }
5
6 // ===== モジュール com.foo.service =====
7 module com.foo.service {
8 requires transitive com.foo.util;
9 exports com.foo.service; // Svc.make() は com.foo.util.Token を返す
10 }
11
12 // ===== モジュール com.foo.app =====
13 module com.foo.app {
14 requires com.foo.service; // ← com.foo.util は requires していない
15 }
com.foo.app 内のコードが、次のように書かれている。正しい記述を選べ。
16 import com.foo.service.Svc; 17 import com.foo.util.Token; // util を requires していないが… 18 Token t = Svc.make(); 19 System.out.println(t.id());
- A
com.foo.appはcom.foo.util.Tokenを参照できない。module-infoにrequires com.foo.util;を足さないとコンパイルエラー。 - B
com.foo.serviceのrequires transitive com.foo.utilによって暗黙的可読性(implied readability)がcom.foo.appへ伝播するため、追加のrequiresなしでTokenを参照でき、コンパイル・実行とも成功する。 - C
com.foo.utilはexportsだけでなくcom.foo.app向けにopensしないとTokenを参照できない。 - D
requires transitiveは不正な構文でありcom.foo.serviceのコンパイル時点でエラーになる。
正解・解説・誤答理由・ひっかけを見る▼ open
✓ 正解:B✓Gold監修
解説
モジュールの requires は「読む(read)」関係を1段だけ作る。
通常 com.foo.app が com.foo.util の型を使うには、app 自身が requires com.foo.util する必要がある。
ところが requires transitive を付けると「自分を読むモジュールにも、その依存先を読ませる」という暗黙的可読性(implied readability)が成立する。
ここでは com.foo.service が requires transitive com.foo.util しているので、
service を読む者(=app)は自動的に util も読める。よって app は requires com.foo.util を書かずとも Token を参照でき、コンパイル・実行とも成功する。
transitive は「公開APIの戻り値・引数に他モジュールの型が露出する」ときに使うのが定石(ここでは Svc.make() が Token を返す)。これを付けないと、service を使う全モジュールが util を個別に requires する羽目になる。
- Aこれは
transitiveが無い(=普通のrequires com.foo.util)場合の挙動。transitive 付きでは追加の requires は不要。 - C
opensは実行時リフレクション用。コンパイル時の通常の型参照には無関係で、exportsで足りている。 - D
requires transitiveは正規の構文(JLS / JPMS)。コンパイルは通る。
ひっかけ: 「使う型のモジュールは必ず自分で requires する」と機械的に覚えていると A を選ぶ。
transitive は依存を“横流し”できる例外。逆に service が transitive を外すと、app は即コンパイルエラー(下記・実機確認の負側)。実機確認の答え合わせ
javac --module-source-path ... --module com.foo.app → コンパイル成功
java --module com.foo.app/...Main → 出力:TKN
【負側の確認】service を「requires com.foo.util(transitive なし)」に変えて再ビルドすると app がコンパイル失敗:
Main.java:17: error: package com.foo.util does not exist
import com.foo.util.Token;
^公式ドキュメント・関連JLS SE21 §7.7.1 Dependences(requires / transitive)↗ModuleDescriptor.Requires.Modifier.TRANSITIVE↗
✓Gold 保有者による書き下ろし解説・実機で検証済