第7章 パッケージとモジュール

目次

7.1 パッケージ・メンバー
7.2 モジュールおよびパッケージのホスト・サポート
7.3 コンパイル・ユニット
7.4 パッケージ宣言
7.4 名前付きパッケージ
7.4 名前のないパッケージ
7.4 パッケージの可観測性と可視性
7.5 インポート宣言
7.5 単一タイプ・インポート宣言
7.5 タイプ- オンデマンド・デクラレーション
7.5 単一静的インポート宣言
7.5 静的オンデマンド・デクラレーション
7.5 単一モジュール・インポート宣言
7.6 最上位クラスおよびインタフェース宣言
7.7 モジュール宣言
7.7 依存関係
7.7 エクスポートおよびオープンされたパッケージ
7.7 サービス消費量
7.7 サービス提供
7.7 名前のないモジュール
7.7 モジュールの監視性

プログラムは一連のパッケージとして編成されます。 パッケージのメンバー(§7.1)は、パッケージのコンパイル単位で宣言されるクラスとインタフェース、および独自のコンパイル単位とサブパッケージを含むサブパッケージです。

各パッケージには、クラスとインタフェースの独自の名前セットがあり、名前の競合を防ぐのに役立ちます。 パッケージの命名体系は階層構造です。

パッケージのセットが十分にまとまっている場合は、パッケージをモジュールにグループ化できます。 モジュールは、そのパッケージの一部またはすべてをエクスポート済として分類します。つまり、モジュール外のコードからクラスおよびインタフェースにアクセスできます。 パッケージがモジュールによってエクスポートされないと、モジュール内のコードのみがそのクラスおよびインタフェースにアクセスできます。 さらに、モジュール内のコードが別のモジュールによってエクスポートされたパッケージにアクセスする場合、最初のモジュールは2番目のモジュールに明示的に依存する必要があります。 したがって、モジュールは、そのパッケージがほかのモジュールをどのように使用するか(依存関係を指定することによって)を制御し、ほかのモジュールがそのパッケージをどのように使用するか(どのパッケージをエクスポートするかを指定することによって)を制御します。

モジュールおよびパッケージは、ファイル・システムまたはデータベース(§7.2)に格納できます。 ファイル・システムに格納されるモジュールおよびパッケージは、単純な実装でモジュール、クラスおよびインタフェース宣言を簡単に検索できるように、コンパイル・ユニットの編成に一定の制約がある場合があります。

コンパイル・ユニット内のコードは、そのパッケージで宣言されたすべてのクラスおよびインタフェースに自動的にアクセスでき、また、事前定義済パッケージjava.langで宣言されたすべてのpublicクラスおよびインタフェースを自動的にインポートします。

クラスまたはインタフェースがpublicと宣言されている場合にのみ、クラスまたはインタフェースを宣言するパッケージ外の最上位クラスまたはインタフェース(§6.6)にアクセスできます。 最上位レベルのクラスまたはインタフェースは、クラスまたはインタフェースがpublicとして宣言され、エクスポートされたパッケージのメンバーである場合にのみ、それを宣言するモジュールの外からアクセスできます。 publicと宣言されているが、エクスポートされたパッケージのメンバーではないクラスまたはインタフェースには、モジュール内のコードからのみアクセスできます。

小規模なプログラムやカジュアルな開発の場合、パッケージは名前なし(§7.4.2)にすることも、単純な名前にすることもできますが、コードが広く配布される場合は、修飾名を使用して一意のパッケージ名を選択する必要があります。 これにより、2つの開発グループが同じパッケージ名を選択し、それらのパッケージがあとで1つのプログラムで使用された場合に発生する競合を防ぐことができます。

7.1.  パッケージ・メンバー

パッケージのメンバーは、そのサブパッケージ、およびパッケージのすべてのコンパイル単位(§7.3)で宣言されたすべてのトップレベルクラス(§8 (Classes))およびトップレベルインタフェース(§9 (Interfaces)です。

たとえば、Java SE Platform APIの場合:

  • パッケージjavaには、awtappletiolangnetおよびutilのサブパッケージがありますが、コンパイル・ユニットはありません。

  • パッケージjava.awtには、imageという名前のサブパッケージと、クラスおよびインタフェースの宣言を含む多数のコンパイル・ユニットがあります。

パッケージの完全修飾名(§6.7)がPで、QPのサブパッケージである場合、P.Qはサブパッケージの完全修飾名であり、さらにパッケージを示します。

パッケージに同じ名前のメンバーが2つ含まれていないか、コンパイル時にエラーが発生します。

いくつか例を挙げます。

  • パッケージjava.awtにはサブパッケージimageがあるため、imageという名前のクラスまたはインタフェースの宣言を含めることはできません(含まない)。

  • mouseという名前のパッケージがあり、そのパッケージにメンバー・クラスButton (mouse.Buttonと呼ばれることもあります)がある場合、完全修飾名mouse.Buttonまたはmouse.Button.Clickのパッケージは存在できません。

  • com.nighthacks.java.jagがクラスの完全修飾名である場合、完全修飾名がcom.nighthacks.java.jagまたはcom.nighthacks.java.jag.scrabbleのパッケージは存在できません。

ただし、異なるパッケージのメンバーが同じ単純名を持つことは可能です。 たとえば、パッケージを宣言できます。


package vector;
public class Vector { Object[] vec; }

パッケージjava.utilVectorという名前のクラスも宣言している場合でも、Vectorという名前のpublicクラスをメンバーとして持ちます。 これらの2つのクラスは異なっており、完全修飾名が異なっている(§6.7)。 この例のVectorの完全修飾名はvector.Vectorですが、java.util.VectorはJava SEプラットフォームに含まれるVectorクラスの完全修飾名です。 パッケージvectorにはVectorという名前のクラスが含まれているため、Vectorという名前のサブパッケージも保持できません。

パッケージの階層命名構造は、関連するパッケージを従来の方法で編成するために便利であることを意図していますが、そのパッケージ内で宣言されている最上位のクラスまたはインタフェース(§7.6)と同じ単純名を持つサブパッケージを持つパッケージに対する禁止以外の意味はありません。

たとえば、oliverという名前のパッケージとoliver.twistという名前の別のパッケージ、またはevelyn.woodevelyn.waughという名前のパッケージの間には、特別なアクセス関係はありません。 つまり、oliver.twistという名前のパッケージ内のコードは、他のパッケージのコードよりも、パッケージoliver内で宣言されたクラスおよびインタフェースに適切にアクセスできません。

7.2. モジュールおよびパッケージのホスト・サポート

各ホストシステムは、モジュール、パッケージ、およびコンパイルユニットの作成方法と格納方法を決定します。

各ホスト・システムは、特定のコンパイルで監視可能なコンパイル・ユニットを決定します(§7.3)。 また、各ホスト・システムは、モジュールに関連付けられる監視可能なコンパイル・ユニットも決定します。 モジュールに関連付けられたコンパイル単位の可観測性により、どのモジュールが可観測性であるか(§7.7.6)、どのパッケージがそれらのモジュール内で可視であるか(§7.4.3)が決まります。

ホスト・システムは、モジュール宣言を含むコンパイル・ユニットが実際は監視可能ではないため、そこで宣言されたモジュールに関連付けられていないことを自由に判断できます。 これにより、コンパイラは、modulesourcepath上のどのディレクトリが特定のモジュールを実際に実装するかを選択できます。 ただし、モジュール宣言を含むコンパイル・ユニットが監視可能であるとホスト・システムが判断した場合、§7.4.3では、コンパイル・ユニットは、他のモジュールではなく、そこで宣言されたモジュールに関連付ける必要があると規定しています。

ホストシステムは、クラスまたはインタフェース宣言を含むコンパイルユニットが(最初に)監視可能であり、かつ(2番目)無名モジュールまたは自動モジュールに関連付けられていることを自由に判断できます。ただし、コンパイルユニット内に無名または自動モジュールが存在する宣言(監視可能、またはそれ以外)はありません。

Java SE Platformの単純な実装では、パッケージおよびコンパイル・ユニットをローカル・ファイル・システムに格納できます。 他の実装では、分散ファイル・システムまたはなんらかの形式のデータベースを使用してこれらを格納できます。

ホスト・システムがパッケージおよびコンパイル・ユニットをデータベースに格納する場合、データベースでは、ファイルベースの実装で許容されるコンパイル・ユニットにオプションの制限(§7.6)を課してはなりません。

たとえば、データベースを使用してパッケージを格納するシステムでは、コンパイル・ユニットごとに最大1つのpublicクラスまたはインタフェースが強制されない場合があります。

ただし、データベースを使用するシステムでは、ファイルベースの実装へのエクスポートを目的として、プログラムを制限に従う形式に変換するオプションを提供する必要があります。

ファイル・システムにパッケージを格納する非常に単純な例として、プロジェクト内のすべてのパッケージおよびソース・コードとバイナリ・コードが単一のディレクトリとそのサブディレクトリに格納される場合があります。 このディレクトリの直近の各サブディレクトリは、最上位レベルのパッケージ、つまり完全修飾名が単一の単純名で構成されるパッケージを表します。 以降の各サブディレクトリ・レベルは、包含ディレクトリなどによって表されるパッケージのサブパッケージを表します。

ディレクトリには、次の即時サブディレクトリが含まれる場合があります。

com
gls
jag
java
wnj

ここで、ディレクトリjavaにはJava SE Platformパッケージが含まれ、ディレクトリjagglsおよびwnjには、この仕様の作成者のうち3人が個人使用のために作成され、この小規模なグループ内で相互に共有するパッケージが含まれる場合があります。また、ディレクトリcomには、§6.1で説明されている規則を使用してパッケージに一意の名前を生成した会社から調達されたパッケージが含まれます。

この例では、ディレクトリjavaには、特に次のサブディレクトリが含まれます。

applet
awt
io
lang
net
util

Java SE Platform APIの一部として定義されているパッケージjava.appletjava.awtjava.iojava.langjava.netおよびjava.utilに対応します。

引き続き例を続けますが、ディレクトリutil内で検索すると、次のファイルが表示される場合があります。

BitSet.java        Observable.java
BitSet.class       Observable.class
Date.java          Observer.java
Date.class         Observer.class
...

.javaファイルには、対応する.classファイルにバイナリ・コンパイル・フォームが含まれているクラスまたはインタフェースの定義を含むコンパイル・ユニット(§7.3)のソースが含まれます。

この単純なパッケージ構成では、Java SEプラットフォームの実装により、パッケージ名のコンポーネントが連結され、隣接するコンポーネント間にファイル名セパレータ(ディレクトリ・インジケータ)が配置されて、パッケージ名がパス名に変換されます。

たとえば、ファイル名セパレータが/のオペレーティング・システムでこの単純な組織が使用された場合、パッケージ名は次のようになります。

jag.scrabble.board

ディレクトリ名に変換されます。

jag/scrabble/board

パッケージ名コンポーネントまたはクラス名には、ホスト名にASCII文字のみを使用できるシステム上のUnicode文字など、ホストファイルシステムの通常のディレクトリ名に正しく表示できない文字が含まれていることがあります。 規則として、\\uxxxxエスケープ(§3.3)のように、文字の数値を示す@文字の後に4桁の16進数が続くなどを使用して、文字をエスケープできます。

この規則では、パッケージ名は次のようになります。

children.activities.crafts.papierM\u00e2ch\u00e9

完全なUnicodeを使用して次のように記述することもできます。

children.activities.crafts.papierMâché

ディレクトリ名にマップできます。

children/activities/crafts/papierM@00e2ch@00e9

@文字が、特定のホスト・ファイル・システムのファイル名で有効な文字でない場合は、識別子で有効でない他の文字をかわりに使用できます。

7.3.  コンパイル・ユニット

CompilationUnitは、Javaプログラムの構文文法(§2.3)のゴール記号(§2.1)です。 次の本番環境で定義されます:

通常のコンパイル・ユニットは、次の3つの部分から構成され、それぞれがオプションです。

  • コンパイル・ユニットが属するパッケージの完全修飾名(§6.7)を指定するpackage宣言(§7.4)。

    package宣言を持たないコンパイル・ユニットは、名前のないパッケージ(§7.4.2)の一部です。

  • import宣言(§7.5)は、他のパッケージからのクラスとインタフェース、およびクラスとインタフェースのstaticメンバーを、その単純な名前を使用して参照できるようにします。

  • クラスおよびインタフェースの最上位宣言(§7.6)。

コンパクト・コンパイル・ユニットは、クラス・メンバー宣言の空でないシーケンスから構成され、少なくとも1つはメソッド宣言(§8.4)で、オプションでimport宣言が前に付きます。 メソッド、フィールド、クラスおよびインタフェースの宣言は、クラス本体(§8.1.7)の場合と同様に、import宣言(存在する場合)の後の任意の順序で許可されます。

これは、次のコンパイル単位があいまいな通常のコンパイル・ユニットであることを意味します。


import java.util.Vector;
class Test { ... }

ただし、次のものはあいまいなコンパクト・コンパイル・ユニットです。


import java.util.Vector;
static void main(){ ... }
class Test { ... }

コンパクト・コンパイル・ユニットは、そのメンバーがコンパクト・コンパイル・ユニットで宣言されたメソッド、フィールド、クラスおよびインタフェースであるトップ・レベル・クラス(§7.6)を暗黙的に宣言します。 クラスの詳細は、§8.1.8に記載されています。

モジュラ・コンパイル・ユニットは、module宣言(§7.7)で構成され、オプションでimport宣言が前に付きます。 import宣言を使用すると、このモジュールおよび他のモジュール内のパッケージからのクラスおよびインタフェース、およびクラスおよびインタフェースのstaticメンバーを、module宣言内の単純名を使用して参照できます。

すべてのコンパイル・ユニットは、事前定義されたパッケージjava.langで宣言されたすべてのpublicクラスまたはインタフェースを暗黙的にインポートします。これは、package宣言の直後に、宣言import java.lang.*;が各コンパイル・ユニットの先頭にあるかのようになります。 その結果、それらのクラスおよびインタフェースすべての名前をそれぞれのコンパイル・ユニットで単純名として使用できます。

すべてのコンパクト・コンパイル・ユニットは、import module java.base;宣言が各コンパクト・コンパイル・ユニット(§7.5.5)の先頭にあるかのように、モジュールjava.baseによってエクスポートされたパッケージのすべてのpublic最上位クラスおよびインタフェースを暗黙的にインポートします。 その結果、これらのすべてのクラスおよびインタフェースの名前は、すべてのコンパクトコンパイルユニットで単純名として使用できます。

ホスト・システムは、事前定義されたパッケージjava内のコンパイル・ユニットと、そのサブパッケージlangおよびio(すべて常に監視可能)を除き、監視可能なコンパイル・ユニットを決定します。

各監視可能コンパイル・ユニットは、次のようにモジュールと関連付けできます。

  • ホスト・システムは、ホスト・システムによって選択されたモジュールに監視可能な通常のコンパイル・ユニットが関連付けられていると判断できます。ただし、(i)事前定義されたパッケージjava内の通常のコンパイル・ユニットとそのサブパッケージlangおよびioは、すべてjava.baseモジュールに関連付けられ、(ii)§7.4.2で指定されたモジュールに関連付けられている無名パッケージ内の通常のコンパイル・ユニット。

  • ホスト・システムは、監視可能なコンパイル・ユニットがコンパイル・ユニットによって宣言されたモジュールに関連付けられていることを確認する必要があります。

コンパイル・ユニットの可観測性はパッケージの可観測性に影響し(§7.4.3)、可観測的コンパイル・ユニットとモジュールとの関連付けはそのモジュールの可観測性に影響します(§7.7.6)。

モジュールMに関連付けられたモジュラ・コンパイル・ユニットと通常のコンパイル・ユニットをコンパイルする場合、ホスト・システムはMの宣言で指定された依存性を考慮する必要があります。 具体的には、ホスト・システムは通常、監視可能なコンパイル単位をMで表示可能なコンパイル単位のみに制限する必要があります。 Mから参照できる通常のコンパイル単位は、Mによって読み取られるモジュールに関連付けられた監視可能な通常のコンパイル単位です。 Mによって読み取られるモジュールは、java.lang.moduleパッケージ仕様部で説明されているように、resolutionの結果によって指定され、Mが唯一のルート・モジュールとなります。 ホスト・システムは、Mによって読み取られるモジュールを特定するために解決を実行する必要があります。java.lang.moduleパッケージ仕様部で説明されているいずれかの理由で解決に失敗した場合は、コンパイル時にエラーが発生します。

可読性リレーションは再帰的であるため、Mは自身を読み取るため、Mに関連付けられたモジュラ・コンパイル・ユニットと通常のコンパイル・ユニットはすべてMから参照できます。

Mによって読み取られるモジュールは、M (§7.4.3)に一意に見えるパッケージを駆動します。このモジュールは、スコープ内の最上位パッケージと、Mに関連付けられたモジュラーおよび通常のコンパイル・ユニット内のコードのパッケージ名の意味の両方を駆動します(§6.3§6.5.3§6.5.5)。

前述のルールによって、モジュラ・コンパイル・ユニット内の注釈(特に、モジュール宣言に適用される注釈)で使用されるパッケージと型の名前が、モジュールに関連付けられた通常コンパイル・ユニットにそれらが出現する場合と同様に解釈されることが確実になります。

異なる通常コンパイル・ユニットで宣言されたクラスおよびインタフェースは、相互を循環的に参照することがあります。 Javaコンパイラは、そのようなすべてのクラスおよびインタフェースを同時にコンパイルするように準備する必要があります。

7.4.  パッケージ宣言

package宣言は、コンパイル・ユニットが属するパッケージを示すために、通常のコンパイル・ユニット内に表示されます。

7.4.1. 名前付きパッケージ

通常のコンパイル・ユニットのパッケージ宣言は、コンパイル・ユニットが属するパッケージの名前(§6.2)を指定します。

PackageDeclaration:
PackageModifier:

package宣言に記載されているパッケージ名は、パッケージの完全修飾名(§6.7)である必要があります。

パッケージ宣言のスコープとシャドウ化は、§6.3および §6.4で規定されています。

パッケージ宣言の注釈修飾子に関する規則は、§9.7.4および§9.7.5で指定されています。

指定されたパッケージには、注釈付きpackage宣言が最大1つ許可されます。

この制限を適用する方法は、実装によって異なります。 ファイルシステムベースの実装では、次のスキームを強くお薦めします。package宣言が存在する場合は、package-info.javaというソース・ファイルに、パッケージのソース・ファイルを含むディレクトリに配置します。 このファイルには、package-infoというクラスのソースは含まれていません。package-infoは有効な識別子ではないため、実際にはそうすることは不正です。 通常、package-info.javaにはpackage宣言のみが含まれ、パッケージ上の注釈が直前に付きます。 パッケージ・アクセスを持つ1つ以上のクラスのソース・コードがファイルに技術的に含まれている可能性がありますが、非常に不適切な形式になります。

package-info.javaが存在する場合は、javadocおよびその他の類似のドキュメント生成システムに対してpackage.htmlのかわりにpackage-info.javaを使用することをお薦めします。 このファイルが存在する場合、ドキュメント生成ツールでは、package-info.javapackage宣言の直前のパッケージ・ドキュメント・コメントを検索する必要があります。 このようにして、package-info.javaはパッケージ・レベルの注釈およびドキュメントの唯一のリポジトリになります。 将来、ほかのパッケージレベルの情報を追加することが望ましい場合、このファイルはこの情報に便利なホームであることが証明されるはずです。

7.4.2.  名前のないパッケージ

コンパクト・コンパイル・ユニット、またはpackage宣言を持たないが、少なくとも1つの他の種類の宣言を持つ通常のコンパイル・ユニットは、名前のないパッケージの一部です。

名前のないパッケージは、Java SEプラットフォームによって主に、小規模または一時アプリケーションの開発時または開発開始時に便宜上提供されます。

名前のないパッケージにはサブパッケージを含めることはできません。package宣言の構文には、常に名前付きの最上位パッケージへの参照が含まれているためです。

Java SEプラットフォームの実装では、少なくとも1つの名前のないパッケージをサポートする必要があります。 実装では、複数の名前のないパッケージをサポートできますが、必須ではありません。 名前のない各パッケージ内の通常のコンパイル・ユニットは、ホスト・システムによって判断されます。

ホストシステムは、無名パッケージ内の通常のコンパイルユニットを、名前付きモジュールではなく無名モジュール(§7.7.5)に関連付ける必要があります。

例7.4.2-1.  名前のないパッケージ

コンパイル・ユニット:

class FirstCall {
    public static void main(String[] args) {
        System.out.println("Mr. Watson, come here. "
                           + "I want you.");
    }
}

名前のないパッケージの一部として、非常に単純なコンパイル単位を定義します。


パッケージの格納に階層ファイル・システムを使用するJava SEプラットフォームの実装では、一般的な戦略の1つは、名前のないパッケージを各ディレクトリに関連付けることです。一度に識別できる名前のないパッケージは1つのみで、つまり「現在の作業ディレクトリ」に関連付けられているパッケージです。 「現在の作業ディレクトリ」の正確な意味は、ホスト・システムによって異なります。

7.4.3. パッケージの可観測性と可視性

次のうち少なくとも1つに該当する場合にのみ、パッケージは監視可能です。

  • パッケージの宣言を含む通常のコンパイル・ユニットは監視可能です(§7.3)。

  • パッケージのサブパッケージが観察可能です。

パッケージjavajava.langおよびjava.ioは常に監視可能です。

これは、前述のルールおよび監視可能なコンパイル・ユニットのルールから、次のように結論付けることができます。 事前定義済パッケージjava.langはクラスObjectを宣言するため、Objectのコンパイル・ユニットは常に監視可能です(§7.3)。 したがって、java.langパッケージは監視可能で、javaパッケージも監視可能です。 さらに、Objectは監視可能であるため、配列型Object[]は暗黙的に存在します。 そのスーパーインタフェースjava.io.Serializable (§10.1)も存在するため、java.ioパッケージは監視可能です。

パッケージの宣言を含む通常のコンパイル・ユニットがMから参照可能である場合のみ、パッケージはモジュールMから参照可能です。

パッケージの可視性は、パッケージが特定のモジュールにとって有用な方法で観察可能であることを意味します。 サブパッケージP.Qが観察可能であるため、パッケージPが観察可能であることを知ることは一般に有用ではありません。 たとえば、P.Qが(モジュールM1で)観察可能であり、P.Rが(モジュールM2で)観察可能であるとします。その後、Pは観察可能ですが、どこですか。 M1、M2、または両方で 質問は冗長です。M1のみを必要とするモジュールNのコンパイル時には、P.Qが観察可能であることは重要ですが、Pが観察可能であることは重要ではありません。

パッケージは、次のいずれかが保持されている場合にのみ、モジュールMから一意に参照できます。

  • Mに関連付けられている通常のコンパイル・ユニットにはパッケージの宣言が含まれ、Mでは、パッケージをMにエクスポートする他のモジュールは読み取られません。

  • Mに関連付けられた通常のコンパイル・ユニットにはパッケージの宣言が含まれず、MはパッケージをMにエクスポートする他のモジュールを正確に1つ読み込みます。

7.5.  インポート宣言

インポート宣言を使用すると、名前付きクラス、インタフェースまたはstaticメンバーを、単一の識別子で構成される単純な名前(§6.2)で参照できます。

適切なインポート宣言を使用しない場合、別のパッケージで宣言されたクラスまたはインタフェースへの参照、または別のクラスまたはインタフェースのstaticメンバーへの参照は、通常、完全修飾名(§6.7)を使用する必要があります。

  • 単一型インポート宣言(§7.5.1)は、正規名(§6.7)を記述して、単一の名前付きクラスまたはインタフェースをインポートします。

  • type-import-on-demand宣言(§7.5.2)は、パッケージ、クラスまたはインタフェースの正規名を記述することにより、名前付きパッケージ、クラスまたはインタフェースのアクセス可能なすべてのクラスおよびインタフェースを必要に応じてインポートします。

  • 単一静的インポート宣言(§7.5.3)は、クラスまたはインタフェースから指定された名前を持つアクセス可能なすべてのstaticメンバーを、その正規名を指定することによってインポートします。

  • static-import-on-demand宣言(§7.5.4)は、クラスまたはインタフェースの正規名を指定することで、必要に応じて名前付きクラスまたはインタフェースのアクセス可能なstaticメンバーをすべてインポートします。

  • 単一モジュール・インポート宣言(§7.5.5)は、必要に応じて、指定されたモジュールによってエクスポートされたパッケージのすべてのアクセス可能なクラスおよびインタフェースをインポートします。

これらの宣言によってインポートされたクラス、インタフェース、またはメンバーのスコープとシャドウは、§6.3および §6.4で指定されています。

import宣言は、クラス、インタフェースまたはメンバーを、実際にimport宣言を含むコンパイル・ユニット内でのみ、単純な名前で使用できるようにします。 import宣言によって導入されたクラス、インタフェースまたはメンバーのスコープには、特に同じパッケージ内の他のコンパイル・ユニット、現在のコンパイル・ユニット内の他のimport宣言、または現在のコンパイル・ユニット内のpackage宣言(package宣言の注釈を除く)は含まれません。

7.5.1.  単一型インポート宣言

単一タイプ・インポート宣言は、標準名を指定して単一のクラスまたはインタフェースをインポートし、単一タイプ・インポート宣言が表示されるコンパイル・ユニットのモジュール、クラスおよびインタフェース宣言の単純な名前で使用できるようにします。

SingleTypeImportDeclaration:
import タイプ名 ;

TypeNameは、クラスまたはインタフェースの正規名(§6.7)である必要があります。

クラスまたはインタフェースは、名前付きパッケージのメンバー、または最も外側の字句で包含するクラスまたはインタフェース宣言(§8.1.3)が名前付きパッケージのメンバーであるクラスまたはインタフェースのメンバーである必要があります。そうでない場合、コンパイル時にエラーが発生します。

指定されたクラスまたはインタフェースにアクセスできない場合、コンパイル時にエラーが発生します(§6.6)。

同じコンパイル・ユニット内の2つの単一タイプ・インポート宣言が、同じ単純名を持つクラスまたはインタフェースをインポートしようとすると、2つのクラスまたはインタフェースが同じでないかぎり、コンパイル時にエラーが発生し、その場合は重複する宣言が無視されます。

単一型インポート宣言によってインポートされたクラスまたはインタフェースが、import宣言を含むコンパイル・ユニットで最上位のクラスまたはインタフェース(§7.6)として宣言されている場合、import宣言は無視されます。

単一型インポート宣言が単純名がxのクラスまたはインタフェースをインポートし、コンパイル・ユニットが単純名がxのトップ・レベル・クラスまたはインタフェースも宣言すると、コンパイル時にエラーが発生します。

コンパイル・ユニットに、単純名がxのクラスまたはインタフェースをインポートする単一型インポート宣言と、単純名がxのクラスまたはインタフェースをインポートする単一静的インポート宣言(§7.5.3)の両方が含まれている場合、2つのクラスまたはインタフェースが同じでないかぎりコンパイル時にエラーが発生し、その場合、重複する宣言は無視されます。

例7.5.1-1.  単一型インポート


import java.util.Vector;

単純名Vectorを、コンパイル・ユニット内のクラスおよびインタフェース宣言内で使用できるようにします。 したがって、単純名Vectorは、フィールド、パラメータ、ローカル変数、または同じ名前のネストされたクラスまたはインタフェース宣言の宣言によって、シャドウ(§6.4.1)または不明瞭(§6.4.2)にならないすべての場所で、パッケージjava.util内のクラス宣言Vectorを参照します。

java.util.Vectorの実際の宣言は汎用(§8.1.2)であることに注意してください。 インポート後、Vectorという名前は、Vector<String>などのパラメータ化された型またはRAW型Vectorの修飾なしで使用できます。 import宣言の関連制限は、汎用クラスまたはインタフェース宣言内で宣言されたメンバー・クラスまたはインタフェースをインポートできるが、その外部型は常に消去されることです。


例7.5.1-2. 重複するクラス宣言

このプログラムでは:


import java.util.Vector;
class Vector { Object[] vec; }

次のように、Vectorの重複宣言が原因でコンパイル時にエラーが発生します。


import java.util.Vector;
import myVector.Vector;

myVectorは、コンパイル・ユニットを含むパッケージです。


package myVector;
public class Vector { Object[] vec; }


例7.5.1-3.  サブパッケージのインポートなし

import宣言は、クラスまたはインタフェースのみで、サブパッケージをインポートできないことに注意してください。

たとえば、java.utilをインポートし、util.Randomという名前を使用してjava.util.Random型を参照することは機能しません。


import java.util;
class Test { util.Random generator; }
  // incorrect: compile-time error


例7.5.1-4.  パッケージ名でもある型名のインポート

パッケージ名と型名は、通常、§6.1で説明されている命名規則では異なります。 ただし、名前がMosquitoのパブリック・クラスを宣言する、従来とは異なる名前を持つパッケージVectorが存在する、次のような例では、次のようになります。

package Vector;
public class Mosquito { int capacity; }

その後、コンパイル単位:

package strange;
import java.util.Vector;
import Vector.Mosquito;
class Test {
    public static void main(String[] args) {
        System.out.println(new Vector().getClass());
        System.out.println(new Mosquito().getClass());
    }
}

クラスVectorをパッケージjava.utilからインポートする単一タイプ・インポート宣言では、パッケージ名Vectorが出現せず、後続のimport宣言で正しく認識されることはありません。 次の例では、コンパイルを行って出力を生成します:

class java.util.Vector
class Vector.Mosquito

7.5.2.  オンデマンド型インポート宣言

type-import-on-demand宣言を使用すると、名前付きパッケージ、クラスまたはインタフェースのアクセス可能なすべてのクラスおよびインタフェースを必要に応じてインポートできます。

TypeImportOnDemandDeclaration:
import PackageOrTypeName . * ;

PackageOrTypeNameは、パッケージ、クラスまたはインタフェースの正規名(§6.7)である必要があります。

PackageOrTypeNameがクラスまたはインタフェース(§6.5.4)を示す場合、そのクラスまたはインタフェースは、指定されたパッケージのメンバーであるか、クラスまたはインタフェース宣言(§8.1.3)が指定されたパッケージのメンバーであるクラスまたはインタフェースのメンバーであるか、コンパイル時にエラーが発生します。

指定されたパッケージが現在のモジュールに一意に見えない場合(§7.4.3)、または指定されたクラスまたはインタフェースにアクセスできない場合(§6.6)は、コンパイル時にエラーが発生します。

type-import-on-demand宣言で、java.langまたは現在のコンパイル・ユニットの名前付きパッケージに名前を付けるのはコンパイル時エラーではありません。 このような場合は、type-import-on-demand宣言は無視されます。

同じコンパイル・ユニット内の2つ以上のタイプ・インポート・オンデマンド宣言で、同じパッケージ、クラスまたはインタフェースに名前を付けることができます。 これらの宣言のいずれか以外はすべて重複しているとみなされ、その型が1回のみインポートされた場合と同じ結果になります。

コンパイル・ユニットに、同じクラスまたはインタフェースに名前を付ける型インポート・オンデマンド宣言と静的インポート・オンデマンド宣言(§7.5.4)の両方が含まれている場合、そのクラスまたはインタフェースのstaticメンバー・クラスおよびインタフェース(§8.5§9.5)が1回のみインポートされるのと同じ効果があります。

例7.5.2-1.  オンデマンド型インポート


import java.util.*;

これにより、パッケージjava.utilで宣言されたすべてのpublicクラスおよびインタフェースの単純名が、コンパイル・ユニットのクラスおよびインタフェース宣言内で使用可能になります。 したがって、単純名Vectorは、そのクラス宣言がシャドウ化されていない(§6.4.1)か不明瞭化されていない(§6.4.2)コンパイル・ユニット内のすべての場所にある、パッケージjava.utilのクラスVectorを参照します。

宣言は、単純名がVectorのクラスまたはインタフェースの単一型インポート宣言、Vectorという名前のクラスまたはインタフェース、コンパイル・ユニットが属するパッケージで宣言されたクラスまたはインタフェース、またはネストされたクラスまたはインタフェースによってシャドウ化される場合があります。

宣言は、Vectorという名前のフィールド、パラメータまたはローカル変数の宣言によって隠すことができます。

(これらの条件のいずれかが発生することは珍しくありません。)


7.5.3.  単一静的インポート宣言

単一静的インポート宣言は、クラスまたはインタフェースから、指定された単純名を持つアクセス可能なすべてのstaticメンバーをインポートします。 これにより、これらのstaticメンバーは、単一静的インポート宣言が出現するコンパイル・ユニットのモジュール、クラスおよびインタフェース宣言の単純な名前で使用できるようになります。

SingleStaticImportDeclaration:
import static タイプ名 . 識別子 ;

TypeNameは、クラスまたはインタフェースの正規名(§6.7)である必要があります。

クラスまたはインタフェースは、名前付きパッケージのメンバー、または最も外側の字句で包含するクラスまたはインタフェース宣言(§8.1.3)が名前付きパッケージのメンバーであるクラスまたはインタフェースのメンバーである必要があります。そうでない場合、コンパイル時にエラーが発生します。

指定されたクラスまたはインタフェースにアクセスできない場合、コンパイル時にエラーが発生します(§6.6)。

識別子は、指定されたクラスまたはインタフェースの少なくとも1つのstaticメンバーに名前を付ける必要があります。 その名前のstaticメンバーがない場合、または名前付きのすべてのメンバーにアクセスできない場合は、コンパイル時にエラーが発生します。

1つの単一静的インポート宣言で、同じ名前を持つ複数のフィールド、クラスまたはインタフェース、あるいは同じ名前とシグネチャを持つ複数のメソッドをインポートできます。 このことが発生するのは、名前付きクラスまたはインタフェースが、すべて同じ名前を持つ複数のフィールド、メンバー・クラス、メンバー・インタフェースまたはメソッドをそれ自体のスーパー・タイプから継承する場合です。

同じコンパイル・ユニット内の2つの単一静的インポート宣言が同じ単純名のクラスまたはインタフェースをインポートしようとすると、コンパイル時にエラーが発生します。ただし、2つのクラスまたはインタフェースが同じである場合は除きます(この場合、重複する宣言は無視されます)。

単一静的インポート宣言が単純名がxのクラスまたはインタフェースをインポートし、コンパイル・ユニットが単純名がxのトップ・レベル・クラスまたはインタフェース(§7.6)も宣言すると、コンパイル時にエラーが発生します。

コンパイル・ユニットに、単純名がxのクラスまたはインタフェースをインポートするsingle-static-import宣言と、単純名がxのクラスまたはインタフェースをインポートするsingle-type-import宣言(§7.5.1)の両方が含まれている場合は、2つのクラスまたはインタフェースが同じでないかぎり、コンパイル時にエラーが発生します。この場合、重複する宣言は無視されます。

7.5.4.  オンデマンド静的インポート宣言

static-import-on-demand宣言を使用すると、指定されたクラスまたはインタフェースのすべてのアクセス可能なstaticメンバーを必要に応じてインポートできます。

StaticImportOnDemandDeclaration:
import static TypeName . * ;

TypeNameは、クラスまたはインタフェースの正規名(§6.7)である必要があります。

クラスまたはインタフェースは、名前付きパッケージのメンバー、または最も外側の字句で包含するクラスまたはインタフェース宣言(§8.1.3)が名前付きパッケージのメンバーであるクラスまたはインタフェースのメンバーである必要があります。そうでない場合、コンパイル時にエラーが発生します。

指定されたクラスまたはインタフェースにアクセスできない場合、コンパイル時にエラーが発生します(§6.6)。

同じコンパイル・ユニット内の2つ以上のオンデマンド静的インポート宣言によって、同じクラスまたはインタフェースの名前が指定されることがあります。この場合、そのような宣言が1つのみ存在する場合と同様の結果になります。

同じコンパイル・ユニットの2つ以上のstatic-import-on-demand宣言が同じメンバーを指定している場合があります。実際は、メンバーが1回だけインポートされた場合と同じです。

1つのstatic-import-on-demand宣言で、同じ名前の複数のフィールド、クラスまたはインタフェース、または同じ名前と署名を持つ複数のメソッドをインポートできます。 このことが発生するのは、名前付きクラスまたはインタフェースが、すべて同じ名前を持つ複数のフィールド、メンバー・クラス、メンバー・インタフェースまたはメソッドをそれ自体のスーパー・タイプから継承する場合です。

コンパイル・ユニットに、同じクラスまたはインタフェースに名前を付けるstatic-import-on-demand宣言とtype-import-on-demand宣言(§7.5.2)の両方が含まれている場合、そのクラスまたはインタフェースのstaticメンバー・クラスおよびインタフェース(§8.5§9.5)が1回のみインポートされるのと同じ効果があります。

7.5.5.  単一モジュール・インポート宣言

単一モジュール・インポート宣言を使用すると、指定したモジュールによってエクスポートされたパッケージのすべてのpublicトップ・レベル・クラスおよびインタフェースを必要に応じてインポートできます。

SingleModuleImportDeclaration:
import module ModuleName ;

単一モジュール・インポート宣言では、次のパッケージのすべてのpublic最上位クラスおよびインタフェースがオンデマンドでインポートされます。

  1. モジュールModuleNameによって現在のモジュールにエクスポートされたパッケージ。

  2. モジュールModuleNameの読取りのために現在のモジュールによって読み取られるモジュールによってエクスポートされるパッケージ。 これにより、プログラムは、別のモジュールからのクラスやインタフェースを参照するモジュールのAPIを使用できるようになります。このとき、該当する別のモジュールのすべてをインポートする必要はありません。

モジュールModuleNameが現在のモジュール(§7.3)によって読み取られない場合、コンパイル時にエラーが発生します。

現在のモジュールによって読み取られるモジュールは、java.lang.moduleパッケージ仕様部(§7.3)で説明されているように、解決の結果によって与えられます。

同じコンパイル・ユニット内の複数の単一モジュール・インポート宣言は、同じモジュールを指名できます。 これらの宣言は1つを除いてすべてが重複しているとみなされます。この結果、そのモジュールは1回のみインポートされたものとして扱われます。

単一モジュール・インポート宣言は、どのソース・ファイルでも使用できます。 ソース・ファイルはモジュールの一部にする必要はありません。 たとえば、モジュールjava.baseおよびjava.sqlは、標準のJavaランタイムの一部であるため、それ自体がモジュールとして開発されていないプログラムによってインポートできます。

パッケージをエクスポートしないモジュールのインポートが役立つ場合があります。 これは、モジュールがパッケージをエクスポートする別のモジュールを間接的に必要とすることがあるためです。 たとえば、java.seモジュールではパッケージはエクスポートされませんが、複数のモジュールがトランジティブに必要になるため、単一モジュール・インポート宣言import module java.se;の効果は、それらのモジュールによってエクスポートされたパッケージを再帰的にインポートすることです。

例7.5.5-1.  通常のコンパイル・ユニットの単一モジュール・インポート

モジュールを使用すると、パッケージのセットをグループ化して1つの名前で再利用できるようになります。また、モジュールのエクスポートされたパッケージは、統合された一貫性のあるAPIを形成することを目的としています。 単一モジュール・インポート宣言を使用すると、開発者はモジュールによってエクスポートされたすべてのパッケージを1回でインポートできるため、モジュラ・ライブラリの再利用が容易になります。 たとえば:


import module java.xml;

これにより、モジュールjava.xmlによってエクスポートされたすべてのパッケージで宣言されたpublicの最上位クラスおよびインタフェースの単純名が、コンパイル・ユニットのクラスおよびインタフェース宣言内で使用可能になります。 したがって、単純名XPathは、モジュールjava.xmlによってエクスポートされたパッケージjavax.xml.xpathのインタフェースXPathを、そのクラス宣言がシャドウ化または不明瞭化されていないコンパイル・ユニットのすべての場所で参照します。

モジュールM0に関連付けられている次のコンパイル・ユニットを想定します。


package q;
import module M1;   // What does this import?
class C { ... }

ここで、モジュールM0には次の宣言があります。


module M0 { requires M1; }

単一モジュール・インポート宣言import module M1;の意味は、M1のエクスポートと、M1が一時的に必要とするモジュールによって異なります。 例として次について考えてみます:


module M1 {
    exports p1;
    exports p2 to M0;
    exports p3 to M3;
    requires transitive M4;
    requires M5;
}

module M3 { ... }

module M4 { exports p10; }

module M5 { exports p11; }

単一モジュール・インポート宣言import module M1;の効果は次のとおりです。

  1. M1p1をすべてのユーザーにエクスポートするため、publicの最上位クラスおよびインタフェースをパッケージp1からインポートします。

  2. M1p2M0 (コンパイル・ユニットが関連付けられているモジュール)にエクスポートするため、publicの最上位クラスおよびインタフェースをパッケージp2からインポートします。

  3. M1にはp10をエクスポートする一時的M4が必要であるため、publicの最上位クラスおよびインタフェースをパッケージp10からインポートします。

パッケージp3またはp11からのものは、コンパイル・ユニットによってインポートされません。

単一モジュール・インポート宣言は、パッケージ宣言のみが含まれているソース・ファイルに記述できます。 このようなファイルは、通常package-info.javaと呼ばれ、パッケージ・レベルの注釈およびドキュメント(§7.4.1)の唯一のリポジトリとして使用されます。


例7.5.5-2.  モジュラ・コンパイル・ユニットの単一モジュール・インポート

インポート宣言は、モジュラ・コンパイル・ユニットにも記述されます。 次のモジュラ・コンパイル・ユニットは、単一モジュール・インポート宣言を使用して、モジュールjava.sqlに関連付けられたインタフェースDriverの単純名をprovidesディレクティブで使用できるようにします。


import module java.sql;
module com.myDB.core {
    exports ...
    requires transitive java.sql;
    provides Driver with com.myDB.greatDriver;
}

モジュールを宣言するモジュール・コンパイル・ユニットでも、そのモジュールをインポートできます。 次の例では、usesディレクティブでクラスCの単純名を使用できることを意味します。


import module M;
module M {
    ...
    exports p;
    ...
    uses C;
    ...
}

ここで、モジュールMによってエクスポートされたパッケージpは次のように宣言されます。


package p;
class C { ... }

単一モジュール・インポート宣言を使用しない場合、クラスCの修飾名をusesディレクティブで使用する必要があります。

次のようなモジュール宣言があるとします:


module M2 {
    requires java.se;
    exports p2;
    ...
}

ここで、M2によってエクスポートされたパッケージp2は次のように宣言されます。


package p2;
import module java.xml;
class MyClass {
    ...
}

モジュールM2はモジュールjava.xmlへの依存性を直接表していませんが、解決プロセスではモジュールjava.xmlがモジュールM2によって読み取られると判断されるため、モジュールjava.xmlのインポートは正しいままです。


例7.5.5-3.  曖昧なインポート

複数のモジュールを明確にインポートすると、次のような名前の曖昧さが発生する可能性があります:


import module java.base;
import module java.desktop;

...
List l = ...        // Error - Ambiguous name!
...

モジュールjava.baseは、public Listインタフェースを持つパッケージjava.utilをエクスポートします。 モジュールjava.desktopは、public Listクラスであるパッケージjava.awtをエクスポートします。 両方のモジュールをインポートすると、単純名Listの使用は明らかに曖昧であり、コンパイル時にエラーが発生します。

ただし、単一のモジュールをインポートするだけでも、次のような名前の曖昧さが発生する可能性があります:


import module java.desktop;

...
Element e = ...     // Error - Ambiguous name!
...

モジュールjava.desktopは、public Elementインタフェースとpublic Elementクラスを持つjavax.swing.textおよびjavax.swing.text.html.parserパッケージをエクスポートします。 したがって、単純名Elementの使用はあいまいであり、コンパイル時にエラーが発生します。

単一型インポート宣言は、名前の曖昧さを解決するために使用できます。 単純名Listがあいまいである前述の例は、次のように解決できます。


import module java.base;
import module java.desktop;

import java.util.List;  // Resolving the ambiguity
                        // of the simple name List

...
List l = ...            // Ok - List is resolved to
                        // java.util.List
...


7.6.  最上位クラスおよびインタフェースの宣言

最上位のクラスまたはインタフェース宣言は、最上位のクラス(§8.1)または最上位のインタフェース(§9.1)を宣言します。

TopLevelClassOrInterfaceDeclaration:

コンパイル・ユニット内のクラスおよびインタフェース宣言のレベルで表示される追加の;トークンは、コンパイル・ユニットの意味に影響しません。 ストレイ・セミコロンは、Javaプログラミング言語では、クラス宣言の後に;を配置するために使用されるC++プログラマの譲歩としてのみ許可されます。 新しいJavaコードでは使用しないでください。

トップ・レベル・クラスは、コンパクト・コンパイル・ユニットによって暗黙的に宣言することもできます(§8.1.8)。

アクセス修飾子がない場合、最上位のクラスまたはインタフェースはパッケージ・アクセスを持ちます。これは、宣言されているパッケージの通常のコンパイル・ユニット内でのみアクセス可能です(§6.6.1)。 クラスまたはインタフェースは、publicと宣言して、同じモジュール内の他のパッケージのコードから、および他のモジュールのパッケージのコードから、クラスまたはインタフェースへのアクセスを付与できます。

最上位のクラスまたはインタフェース宣言に、protectedprivateまたはstaticのいずれかのアクセス修飾子が含まれている場合は、コンパイル時にエラーが発生します。

最上位レベルのクラスまたはインタフェースの名前が、同じパッケージで宣言されている他の最上位レベルのクラスまたはインタフェースの名前として表示される場合は、コンパイル時エラーです。

最上位のクラスまたはインタフェースのスコープとシャドウは、§6.3および §6.4で規定されています。

最上位のクラスまたはインタフェースの完全修飾名は、§6.7で指定されています。

例7.6-1. 競合するトップレベルクラスとインタフェース宣言

package test;
import java.util.Vector;
class Point {
    int x, y;
}
interface Point {  // compile-time error #1
    int getR();
    int getTheta();
}
class Vector { Point[] pts; }  // compile-time error #2

この場合、最初のコンパイル時エラーは、同じパッケージ内のクラスとインタフェースの両方としてのPointという名前の重複宣言によって発生します。 2つ目のコンパイル時エラーは、クラス宣言と単一型インポート宣言の両方で、Vectorという名前を宣言しようとすることです。

ただし、クラス宣言内の名前がクラスまたはインタフェースと重複することはエラーではありません。そうしないと、同じコンパイル・ユニット内の要求時型インポート宣言(§7.5.2)によってインポートされる可能性があります。 したがって、このプログラムでは次のようになります:

package test;
import java.util.*;
class Vector {}  // not a compile-time error

クラスVectorの宣言は、クラスjava.util.Vectorも存在する場合でも許可されます。 このコンパイル・ユニット内では、単純名Vectorはクラスtest.Vectorを参照し、java.util.Vectorを参照しません(これはコンパイル・ユニット内のコードで参照できますが、完全修飾名でのみ参照できます)。


例7.6-2.  最上位クラスおよびインタフェースのスコープ

package points;
class Point {
    int x, y;           // coordinates
    PointColor color;   // color of this point
    Point next;         // next point with this color
    static int nPoints;
}
class PointColor {
    Point first;        // first point with this color
    PointColor(int color) { this.color = color; }
    private int color;  // color components
}

このプログラムは、クラス・メンバーの宣言で相互に使用する2つのクラスを定義します。 クラスPointおよびPointColorには、現在のコンパイル・ユニットのすべてのクラス宣言を含む、パッケージpoints内のすべてのクラス宣言がスコープとして含まれているため、このプログラムは正しくコンパイルされます。 つまり、前方参照は問題ではありません。


例7.6-3.  完全修飾名


class Point { int x, y; }

このコードでは、クラスPointpackage宣言のないコンパイル・ユニットで宣言されるため、Pointは完全修飾名ですが、コードでは次のようになります。


package vista;
class Point { int x, y; }

クラスPointの完全修飾名はvista.Pointです。 (パッケージ名vistaは、ローカルまたは個人での使用に適しています。パッケージが広く配布されることを意図している場合は、一意のパッケージ名を指定することをお薦めします(§6.1)。)


Java SE Platformの実装では、パッケージ内のクラスおよびインタフェースを、その包含モジュール名とバイナリ名の組合せによって追跡する必要があります(§13.1)。 クラスまたはインタフェースに名前を付ける複数の方法をバイナリ名に拡張して、そのような名前が同じクラスまたはインタフェースを参照していることを確認する必要があります。

たとえば、コンパイル・ユニットに単一型インポート宣言(§7.5.1)が含まれている場合、次のようになります。


import java.util.Vector;

次に、そのコンパイル・ユニット内で、単純名Vectorと完全修飾名java.util.Vectorが同じクラスを参照します。

パッケージがファイル・システム(§7.2)に格納されている場合のみ、ホスト・システムは、クラスがコンパイル時エラーであるという制限を強制することを選択できます。または、次のいずれかに該当する場合、クラス名またはインタフェース名に拡張子(.java.javなど)を加えた名前でインタフェースがファイルに見つかりません。

  • クラスまたはインタフェースは、クラスまたはインタフェースが宣言されているパッケージの他の通常のコンパイル・ユニットのコードによって参照されます。

  • クラスまたはインタフェースはpublicとして宣言されます(したがって、他のパッケージのコードからアクセスできる可能性があります)。

この制限は、コンパイル・ユニットごとにこのようなクラスまたはインタフェースが最大1つ必要であることを意味します。 この制限により、Javaコンパイラはパッケージ内の名前付きクラスまたはインタフェースを簡単に見つけることができます。 実際には、多くのプログラマは、publicであるか、他のコンパイル・ユニットのコードによって参照されるかにかかわらず、各クラスまたはインタフェースを独自のコンパイル・ユニットに配置することを選択します。

たとえば、publicクラスwet.sprocket.Toadのソース・コードは、ディレクトリwet/sprocketのファイルToad.javaにあり、対応するオブジェクト・コードは、同じディレクトリのファイルToad.classにあります。

7.7.  モジュール宣言

モジュール宣言は、新しい名前付きモジュールを指定します。 名前付きモジュールは、他のモジュールに対する依存を指定して、独自のコードで使用可能なクラスおよびインタフェースの領域を定義します。また、依存を指定する他のモジュールで使用可能なクラスおよびインタフェースの領域を移入するために、そのパッケージのエクスポートまたはオープンを指定します。

依存性とは、ディレクティブで指定された名前を持つモジュールが存在するかどうかに関係なく、requiresディレクティブによって表されるものです。 依存性とは、特定のrequiresディレクティブの解決(java.lang.moduleパッケージ仕様部を参照)によって列挙される可観測モジュールです。 一般に、Javaプログラミング言語のルールは、依存関係よりも依存関係に関心があります。

ModuleDeclaration:
{Annotation} [open] module Identifier {. 識別子} { {ModuleDirective} }

モジュール宣言には、モジュール間の関係を表現するために他のモジュール宣言で使用できるモジュール名が導入されています。 モジュール名は、.トークンで区切られた1つ以上のJava識別子(§3.8)で構成されます。

モジュールには、標準モジュールオープン・モジュールの2種類があります。 モジュールの種類によって、モジュール外のコードについて、モジュールのタイプおよびそれらのタイプのメンバーへのアクセスの性質が決まります。

open修飾子を使用しない通常のモジュールは、コンパイル時および実行時に、明示的にエクスポートされるパッケージ内の型にのみアクセス権を付与します。

open修飾子を持つオープン・モジュールは、すべてのパッケージがエクスポートされたかのように、明示的にエクスポートされたパッケージ内のタイプのみにコンパイル時にアクセス権を付与しますが、実行時にすべてのパッケージ内のタイプにアクセス権を付与します。

モジュール外部のコード(モジュールが通常であるかオープンであるかに関係なく)の場合、モジュールのエクスポートされたパッケージ内の型に対するコンパイル時または実行時に付与されるアクセスは、それらのパッケージのpublicおよびprotected型と、それらの型のpublicおよびprotectedメンバー(§6.6)専用です。 エクスポートされないパッケージ内の型またはそのメンバーに対して、コンパイル時または実行時にアクセス権は付与されません。 モジュール内のコードは、コンパイル時と実行時にモジュール内のすべてのパッケージで、publicおよびprotected型と、それらの型のpublicおよびprotectedメンバーにアクセスできます。

コンパイル時および実行時にアクセスする場合とは異なり、Java SE PlatformはCore Reflection API (§1.4)を介して反射的アクセスを提供します。 通常のモジュールは、明示的にエクスポートまたは明示的にオープン(あるいはその両方)されたパッケージのみで、型に反射的アクセス権を付与します。 オープン・モジュールは、すべてのパッケージが開かれているかのように、そのすべてのパッケージ内のタイプに反射的アクセス権を付与します。

通常モジュール外のコードの場合、モジュールのエクスポートされた(オープンされていない)パッケージ内の型に付与されるリフレクティブ・アクセスは、これらのパッケージのpublicおよびprotected型、およびそれらの型のpublicおよびprotectedメンバー専用です。 モジュールの開いているパッケージ内の型に付与されるリフレクティブ・アクセス(エクスポートされるかどうかに関係なく)は、それらのパッケージ内のすべての型、およびそれらの型のすべてのメンバーに対するものです。 エクスポートまたはオープンされていないパッケージ内のタイプまたはそのメンバーには、リフレクティブ・アクセス権が付与されません。 モジュール内のコードは、モジュール内のすべてのパッケージ内のすべてのタイプとそのすべてのメンバーへの反射的なアクセスを提供します。

オープン・モジュール外のコードの場合、モジュールのオープン・パッケージ内の型(つまり、モジュール内のすべてのパッケージ)に付与されるリフレクティブ・アクセスは、それらのパッケージ内のすべての型およびそれらの型のすべてのメンバーに対するものです。 モジュール内のコードは、モジュール内のすべてのパッケージ内のすべてのタイプとそのすべてのメンバーへの反射的なアクセスを提供します。

モジュール宣言のディレクティブは、モジュールが他のモジュール(requires§7.7.1を使用)、モジュールが他のモジュールで使用可能にするパッケージ(exportsおよびopens§7.7.2を使用)、使用するサービス(uses§7.7.3を使用)、およびモジュールが提供するサービス(provides§7.7.4を使用)に依存することを示します。

ModuleDirective:
requires {RequiresModifier} ModuleName ;
exports PackageName [to ModuleName {, ModuleName}] ;
opens PackageName [to ModuleName {, ModuleName}] ;
uses TypeName ;
provides TypeName with TypeName {, TypeName} ;
RequiresModifier:
(いずれか)
transitive static

パッケージがファイル・システム(§7.2)に格納されている場合のみ、ホスト・システムは、モジュール宣言がmodule-infoと拡張子(.java.javなど)で構成されるファイルに見つからない場合、コンパイル時エラーであるという制限を強制するように選択できます。

わかりやすくするために、モジュール宣言でディレクティブをグループ化することは必須ではありませんが、モジュールに関連するrequiresディレクティブは、パッケージに関連するexportsおよびopensディレクティブと、サービスに関連するusesおよびprovidesディレクティブと視覚的に区別されるようにします。 たとえば:

module com.example.foo {
    requires com.example.foo.http;
    requires java.logging;

    requires transitive com.example.foo.network;

    exports com.example.foo.bar;
    exports com.example.foo.internal to com.example.foo.probe;

    opens com.example.foo.quux;
    opens com.example.foo.internal to com.example.foo.network,
                                      com.example.foo.probe;

    uses com.example.foo.spi.Intf;
    provides com.example.foo.spi.Intf with com.example.foo.Impl;
}

opensディレクティブは、モジュールが開いている場合に回避できます。

open module com.example.foo {
    requires com.example.foo.http;
    requires java.logging;

    requires transitive com.example.foo.network;

    exports com.example.foo.bar;
    exports com.example.foo.internal to com.example.foo.probe;

    uses com.example.foo.spi.Intf;
    provides com.example.foo.spi.Intf with com.example.foo.Impl;
}

Javaプログラミング言語用の開発ツールでは、requires transitiveディレクティブおよび修飾されていないexportsディレクティブを、モジュールのプライマリAPIとして強調表示することをお薦めします。

7.7.1. 依存関係

requiresディレクティブは、現在のモジュールが依存しているモジュールの名前を指定します。

requiresディレクティブは、java.baseモジュールの宣言には指定しないでください。指定しないと、コンパイル時にエラーが発生します。これは、これが原始モジュールであり、依存性がないためです(§8.1.4)。

モジュールの宣言がjava.baseモジュールへの依存を表現せず、モジュール自体がjava.baseでない場合、モジュールは暗黙的にjava.baseモジュールへの依存性を宣言します。

requiresキーワードの後に修飾子transitiveを付けることができます。 これにより、現在のモジュールrequiresが、requires transitiveディレクティブで指定されたモジュールへの暗黙的に宣言された依存性を保持するモジュールが発生します。

requiresキーワードの後に修飾子staticを付けることができます。 これは、コンパイル時に依存関係が必須であるのに対し、実行時にはオプションであることを指定します。

requiresキーワードの後に同じ修飾子が複数回出現すると、コンパイル時にエラーが発生します。

モジュールの宣言がjava.baseモジュールへの依存を表現しており、そのモジュール自体がjava.baseではない場合、static修飾子がrequiresキーワードの後に出現すると、コンパイル時にエラーが発生します。

モジュール宣言内の複数のrequiresディレクティブで同じモジュール名が指定されている場合、コンパイル時にエラーが発生します。

現在のモジュールが唯一のルート・モジュールであるjava.lang.moduleパッケージ仕様部で説明されているように、解決がjava.lang.moduleパッケージ仕様部で説明されているいずれかの理由で失敗した場合、コンパイル時にエラーが発生します。

たとえば、requiresディレクティブで、監視できないモジュールを指定する場合、または現在のモジュールが直接的または間接的に自身への依存を表現する場合です。

解決が成功すると、結果によって、現在のモジュールによって読み取られるモジュールが指定されます。 現在のモジュールによって読み取られるモジュールによって、現在のモジュールから参照できる通常のコンパイル単位が決まります(§7.3)。 これらの通常のコンパイル単位で宣言された型(およびそれらの通常のコンパイル単位のみ)は、現在のモジュール(§6.6)のコードからアクセスできる場合があります。

Java SEプラットフォームでは、明示的に宣言された名前付きモジュール(モジュール宣言付き)と、暗黙的に宣言された名前付きモジュール(自動モジュール)が区別されます。 ただし、Javaプログラミング言語では区別されません。requiresディレクティブは、明示的に宣言されたか暗黙的に宣言されたかに関係なく、名前付きモジュールを参照します。

自動モジュールは移行に便利ですが、作成者が明示的に宣言されたモジュールに変換した場合、名前とエクスポートされたパッケージが変更される可能性があるという意味で信頼性がありません。 Javaコンパイラは、requiresディレクティブが自動モジュールを参照する場合に警告を発行することをお薦めします。 ディレクティブにtransitive修飾子が含まれている場合は、特に強力な警告が推奨されます。

例7.1.1-1 requires transitiveディレクティブの解決

次の4つのモジュール宣言があるとします:

module m.A {
    requires m.B;
}

module m.B {
    requires transitive m.C;
}

module m.C {
    requires transitive m.D;
}

module m.D {
    exports p;
}

ここで、m.Dによってエクスポートされたパッケージpは次のように宣言されます。

package p;
public class Point {}

また、モジュールm.Aのパッケージclientは、エクスポートされたパッケージppublicPointを参照します。

package client;
import p.Point;
public class Test {
    public static void main(String[] args) {
        System.out.println(new Point());
    }
}

これらのモジュールは、現在のディレクトリにモジュールごとに1つの(含まれるモジュールにちなんで名付けられた)サブディレクトリがあると想定して、次のようにコンパイルできます:

javac --module-source-path . -d . --module m.D
javac --module-source-path . -d . --module m.C
javac --module-source-path . -d . --module m.B
javac --module-source-path . -d . --module m.A

プログラムclient.Testは、次のように実行できます。

java --module-path . --module m.A/client.Test

m.Am.Dを読み取り、m.DPointを含むパッケージをエクスポートするため、m.A内のコードからm.D内のエクスポートされたpublicPointへの参照は有効です。 解決では、m.Aが次のようにm.Dを読み取ることが決定されます。

  • m.A requires m.Bであるため、m.Bを読み取ります。

  • m.Am.Bを読み取るため、m.B requires transitive m.C以降は、m.Am.Cを読み取ることを決定します。

  • その後、m.Am.Cを読み取り、m.C requires transitive m.D以降、m.Am.Dを読み取ることを解決が決定します。

実際には、モジュールは、任意の量のリファクタをサポートするために、複数のレベルの依存関係を介して別のモジュールを読み取ることができます。 モジュールが(requiresを介して)再利用できるようにリリースされると、モジュールの作成者はその名前とAPIにコミットしましたが、元のモジュールが(requires transitiveを介して)コンシューマのために再利用する他のモジュールにコンテンツを自由にリファクタできます。 前述の例では、パッケージpはもともとm.B (つまり、m.A requires m.B)によってエクスポートされている可能性がありますが、リファクタにより、m.Bのコンテンツの一部がm.Cおよびm.Dに移動しました。 requires transitiveディレクティブの連鎖を使用することで、m.Bm.Cおよびm.Dのファミリは、m.Arequiresディレクティブを変更することなく、m.A内のコードのpパッケージへのアクセスを保持できます。 m.Dのパッケージpは、m.Cおよびm.Bによって再エクスポートされるのではなく、m.Dを直接読み取るためにm.Aが作成されることに注意してください。


7.7.2. エクスポートおよびオープンされたパッケージ

exportsディレクティブは、現在のモジュールによってエクスポートされるパッケージの名前を指定します。 他のモジュールのコードの場合、コンパイル時および実行時に、パッケージ内のpublicおよびprotected型、およびそれらの型のpublicおよびprotectedメンバー(§6.6)にアクセス権を付与します。 また、他のモジュールのコードに対して、これらのタイプおよびメンバーへのリフレクティブ・アクセス権を付与します。

opensディレクティブは、現在のモジュールによってオープンされるパッケージの名前を指定します。 他のモジュールのコードの場合、これは実行時に、コンパイル時にではなく、パッケージ内のpublicおよびprotected型、およびそれらの型のpublicおよびprotectedメンバーにアクセス権を付与します。 また、パッケージ内のすべてのタイプ、およびそのすべてのメンバーに、他のモジュールのコードに対するリフレクティブ・アクセス権を付与します。

exportsで指定されたパッケージが、現在のモジュール(§7.3)に関連付けられたコンパイル・ユニットによって宣言されていない場合、コンパイル時にエラーが発生します。

opensでは、現在のモジュールに関連付けられたコンパイル・ユニットによって宣言されていないパッケージを指定できます。 (パッケージが別のモジュールに関連付けられた監視可能なコンパイル・ユニットによって宣言される必要がある場合、opensディレクティブはその他のモジュールには影響しません。)

モジュール宣言内の複数のexportsディレクティブで同じパッケージ名が指定されている場合、コンパイル時にエラーが発生します。

モジュール宣言内の複数のopensディレクティブで同じパッケージ名が指定されている場合、コンパイル時にエラーが発生します。

オープン・モジュールの宣言にopensディレクティブが含まれている場合、コンパイル時にエラーが発生します。

exportsまたはopensディレクティブにto句がある場合、ディレクティブは修飾されます。それ以外の場合、非修飾です。 修飾ディレクティブの場合、パッケージ内のpublicおよびprotected型と、そのpublicおよびprotectedメンバーには、to句で指定されたモジュール内のコードに対してのみアクセスできます。 to句で指定されるモジュールは、現在のモジュールの友人と呼ばれます。 修飾されていないディレクティブの場合、これらの型とそのメンバーには、どのモジュール内のコードでもアクセスできます。

exportsまたはopensディレクティブのto句で、監視できないモジュールを指定することが許可されています(§7.7.6)。

特定のexportsディレクティブのto句で同じモジュール名が複数回指定されている場合、コンパイル時にエラーが発生します。

特定のopensディレクティブのto句で同じモジュール名が複数回指定されている場合、コンパイル時にエラーが発生します。

7.7.3.  サービス消費

usesディレクティブは、現在のモジュール内のコードがjava.util.ServiceLoaderを介してプロバイダを検出できるサービスを指定します。

usesディレクティブがenumクラス(§8.9)を指定した場合、コンパイル時にエラーが発生します。

サービスは、現在のモジュールまたは別のモジュールで宣言できます。 サービスが現在のモジュールで宣言されていない場合、サービスは現在のモジュール内のコードに対してアクセス可能である必要があります(§6.6)。そうしないと、コンパイル時にエラーが発生します。

モジュール宣言内の複数のusesディレクティブで同じサービスが指定されている場合、コンパイル時にエラーが発生します。

7.7.4.  サービスのプロビジョニング

providesディレクティブは、with句が1つ以上のサービス・プロバイダjava.util.ServiceLoaderに指定するサービスを指定します。

providesディレクティブがenumクラス(§8.9)をサービスとして指定すると、コンパイル時にエラーが発生します。

サービスは、現在のモジュールまたは別のモジュールで宣言できます。 サービスが現在のモジュールで宣言されていない場合、サービスは現在のモジュール内のコードに対してアクセス可能である必要があります(§6.6)。そうしないと、コンパイル時にエラーが発生します。

すべてのサービス・プロバイダは、最上位レベルまたはstaticのいずれかのpublicクラスまたはインタフェースである必要があります。そうしないと、コンパイル時にエラーが発生します。

すべてのサービス・プロバイダを現在のモジュールで宣言する必要があります。そうしないと、コンパイル時にエラーが発生します。

サービス・プロバイダが仮パラメータのないpublicコンストラクタを明示的に宣言する場合、またはpublicデフォルト・コンストラクタを暗黙的に宣言する場合(§8.8.9)、そのコンストラクタはプロバイダ・コンストラクタと呼ばれます。

サービス・プロバイダが仮パラメータなしでproviderというpublic staticメソッドを明示的に宣言した場合、そのメソッドはプロバイダ・メソッドと呼ばれます。

サービス・プロバイダにプロバイダ・メソッドがある場合、その戻り型は、(i)現在のモジュールで宣言されるか、別のモジュールで宣言され、現在のモジュール内のコードからアクセス可能である、(ii)providesディレクティブで指定されたサービスのサブタイプである必要があり、そうでない場合はコンパイル時にエラーが発生します。

providesディレクティブで指定されたサービス・プロバイダは現在のモジュールで宣言する必要がありますが、そのプロバイダ・メソッドには、anotherモジュールで宣言された戻り型がある場合があります。 また、サービス・プロバイダがプロバイダ・メソッドを宣言する場合、サービス・プロバイダ自体がサービスのサブタイプである必要はありません。

サービス・プロバイダにプロバイダ・メソッドがない場合、そのサービス・プロバイダはプロバイダ・コンストラクタを持ち、providesディレクティブで指定されたサービスのサブタイプである必要があります。そうしないと、コンパイル時にエラーが発生します。

モジュール宣言内の複数のprovidesディレクティブで同じサービスが指定されている場合、コンパイル時にエラーが発生します。

特定のprovidesディレクティブのwith句で同じサービス・プロバイダが複数回指定されている場合、コンパイル時にエラーが発生します。

7.7.5. 名前のないモジュール

ホスト・システムが名前付きモジュール(§7.3)に関連付けない監視可能な通常のコンパイル・ユニットは、名前なしモジュールに関連付けられています。

名前のないモジュールは、Java SE 9より前に開発されたプログラムが名前付きモジュールを宣言できなかったことを認識して、Java SEプラットフォームによって提供されます。 また、名前のないパッケージを提供するJava SE Platformの理由(§7.4.2)は、主に名前のないモジュールに適用できます。

Java SEプラットフォームの実装では、1つ以上の名前のないモジュールをサポートする必要があります。 実装では、複数の名前のないモジュールがサポートされる場合がありますが、サポートする必要はありません。 各名前のないモジュールに関連付けられている通常のコンパイル単位は、ホストシステムによって決定されます。

ホストシステムは、名前付きパッケージ内の通常のコンパイルユニットを名前なしモジュールに関連付けることができます。

名前のないモジュールのルールは、次のように、名前付きモジュールとの相互運用性を最大化するように設計されています。

  • 名前のないモジュールは、すべての監視可能なモジュール(§7.7.6)を読み取ります。

    名前のないモジュールに関連付けられた通常のコンパイル・ユニットが監視可能であるため、関連付けられた名前のないモジュールが監視可能です。 したがって、Java SEプラットフォームの実装で複数の名前のないモジュールがサポートされている場合、すべての名前のないモジュールは監視可能であり、名前のない各モジュールは、それ自体を含む名前のないすべてのモジュールを読み取ります。

    ただし、名前のないモジュールの通常のコンパイル単位は、名前付きモジュール(§7.3)に対して表示されないことに注意することが重要です。これは、名前付きモジュールが名前なしモジュールを読み取るように配置できるrequiresディレクティブがないためです。 Java SEプラットフォームのコア・リフレクションAPIを使用して、名前付きモジュールが実行時に名前なしモジュールを読み取るように配置できます。

  • 名前のないモジュールは、その名前のないモジュールに通常のコンパイルユニットが関連付けられているすべてのパッケージをエクスポートします。

  • 名前のないモジュールは、その名前のないモジュールに通常のコンパイル・ユニットが関連付けられているすべてのパッケージを開きます。

7.7.6. モジュールの監視性

モジュールは、次のうち少なくとも1つに当てはまる場合に監視可能です。

  • モジュールの宣言を含むモジュラー・コンパイル・ユニットは観察可能です(§7.3)。

  • モジュールに関連付けられた通常のコンパイル・ユニットは監視可能です。