4 モジュール・インポート宣言

モジュールによってエクスポートされたすべてのパッケージを1つの宣言でインポートできます。

ノート:

これはプレビュー機能です。プレビュー機能は、設計、仕様および実装が完了したが、永続的でない機能です。プレビュー機能は、将来のJava SEリリースで、異なる形式で存在することもあれば、まったく存在しないこともあります。プレビュー機能が含まれているコードをコンパイルして実行するには、追加のコマンド行オプションを指定する必要があります。『Preview Language and VM Features』を参照してください。モジュール・インポート宣言の詳細は、JEP 494を参照してください

4つのクラスをインポートする次の例について考えてみます:

import java.util.Map;    
import java.util.function.Function;  
import java.util.stream.Collectors;  
import java.util.stream.Stream;    

public class FruitMap {
    public static void main(String[] args) {
        String[] fruits = new String[] { "apple", "berry", "citrus" };
        Map<String, String> m = Stream
            .of(fruits)
            .collect(Collectors.toMap(
                s -> s.toUpperCase().substring(0,1),
                Function.identity()));
        m.forEach((k, v) ->
            System.out.println(k + " " + v));
    }
}

この例にある4つの単一型インポート宣言をオンデマンド型インポート宣言に置き換えることができます。しかし、そのうちの3つはまだ必要です

import java.util.*;    
import java.util.function.*;  
import java.util.stream.*;

モジュールjava.baseはパッケージjava.utiljava.util.functionおよびjava.util.streamをエクスポートするため、この3つの宣言を1つのモジュール・インポート宣言に置き換えることができます:

import module java.base;

モジュール・インポート宣言の形式は次のとおりです:

import module M;

次のものに含まれる最上位のpublicクラスおよびインタフェースのすべてをオンデマンドでインポートします:

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

  • モジュールMの読取りのために現在のモジュールによって読み取られるモジュールによってエクスポートされたパッケージ。これにより、プログラムは他のモジュールのクラスおよびインタフェースを参照するモジュールのAPIを使用できるため、他のモジュールをすべてインポートする必要がなくなります。

    たとえば、モジュール・インポート宣言import module java.sqlには、import java.sql.*java.sqlモジュール(パッケージjava.loggingおよびjava.xmlを含む)の間接エクスポートのためのオンデマンド・インポートを合せて使用する場合と同じ効果があります。

あいまいなインポート

モジュール・インポート宣言を使用して、異なるパッケージから同じ単純名を持つクラスをインポートすることは可能です。ただし、これはコンパイル時のエラーにつながる可能性があります。次の例では、java.awt.Listjava.util.Listの両方を使用しています:

import java.awt.Frame;
import java.awt.Label;
import java.awt.List;
import java.awt.event.WindowAdapter;   
import java.awt.event.WindowEvent;    
import java.util.Arrays;

public class FruitApp {    
      
    FruitApp(java.util.List<String> fruits) {  
        Frame f = new Frame();  
        Label l = new Label("Fruits");   
        List lst = new List();
        fruits.forEach(i -> lst.add(i));
        l.setBounds(20, 40, 80, 30);  
        lst.setBounds(20, 70, 80, 80);  
        f.add(l);
        f.add(lst);  
        f.setSize(200,200);  
        f.setTitle("Fruit");   
        f.setLayout(null);   
        f.setVisible(true);  

        f.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                System.exit(0);
            }
        });      
    }    
      
    public static void main(String args[]) {   
        String[] fruits = new String[] { "apple", "berry", "citrus" };
        FruitApp f = new FruitApp(Arrays.asList(fruits));    
    }  
}

単一型インポート宣言を次のモジュール・インポート宣言に置き換えるとします:

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

次のようなコンパイル時のエラーが発生します:

FruitApp.java:8: error: reference to Label is ambiguous
        Label l = new Label("Fruits");
        ^
  both interface java.lang.classfile.Label in java.lang.classfile and class java.awt.Label in java.awt match
FruitApp.java:8: error: reference to Label is ambiguous
        Label l = new Label("Fruits");
                      ^
  both interface java.lang.classfile.Label in java.lang.classfile and class java.awt.Label in java.awt match
FruitApp.java:9: error: reference to List is ambiguous
        List lst = new List();
        ^
  both interface java.util.List in java.util and class java.awt.List in java.awt match
FruitApp.java:9: error: reference to List is ambiguous
        List lst = new List();
                       ^
  both interface java.util.List in java.util and class java.awt.List in java.awt match

単純名Labelはあいまいです。java.lang.classfilejava.awtに含まれており、これらはモジュールjava.baseおよびjava.desktopによってそれぞれエクスポートされます。この問題は、単純名Listにも同様に当てはまります。

特定のスコープ(内部クラスやメソッド定義など)内の型(メンバー変数やパラメータ名など)の宣言が、その外側のスコープ内の別の宣言と同じ名前を持つ場合、その宣言は外側のスコープの宣言をシャドウイングします

これらのあいまいさを解決するには、単一型インポート宣言を使用して、コードで使用される単純名の正規名を指定しますこれらの宣言は、import module宣言によってインポートされたLabelクラスおよびListクラスをシャドウイングします。

import module java.desktop;
import module java.base;
import java.awt.Label;
import java.awt.List;

宣言は、その外側のスコープ内の別の宣言と名前が同じ場合、その宣言をシャドウイングします。シャドウイングする側の宣言はその単純な名前で参照できますが、シャドウイングされる側の宣言は参照できません。この例で示すように、シャドウイングは単一型インポート宣言でも発生する可能性があります。

オンデマンド型インポート宣言で、import module宣言をシャドウイングすることもできます。たとえば、FruitAppの例では、オンデマンド型インポート宣言を使用してimport文の数を減らすことができます:

import module java.desktop;
import module java.base;
import java.awt.*;

暗黙的に宣言されたクラスおよびjava.baseモジュール

暗黙的に宣言されたすべてのクラス(「単純なソース・ファイルの作成」を参照)は、java.baseモジュールによってエクスポートされたすべてのパッケージに含まれる最上位のパブリック・クラスおよびインタフェースをすべてオンデマンドでインポートします。これは、オンデマンド型インポート宣言import java.lang.*がすべての通常のクラスの先頭に出現するのではなく、モジュール・インポート宣言import module java.baseが暗黙的に宣言されたすべてのクラスの先頭に出現する場合と同じ動作です。たとえば、暗黙的に宣言されたクラスを使用して、FruitMapの例を次のように簡略化できます:

void main() {
    String[] fruits = new String[] { "apple", "berry", "citrus" };
    Map<String, String> m = Stream
        .of(fruits)
        .collect(Collectors.toMap(
            s -> s.toUpperCase().substring(0,1),
                Function.identity()));
    m.forEach((k, v) ->
        System.out.println(k + " " + v));
}

java.seモジュールのインポートおよびjava.baseモジュールへの推移的依存関係の宣言

java.seモジュールをインポートして、java.baseモジュールに含まれるすべてのパッケージを含む標準Java API全体をインポートできます。これにより、FruitAppの例では、インポート文の数をさらに減らすことができます:

import module java.se;
import java.awt.*;

この更新されたインポート文のリストを使用し、--add-modules java.seオプションを指定してFruitAppをコンパイルします。java.seモジュールは、無名モジュールのルート・モジュールのデフォルト・セットの一部ではありません。

JDK 24より前は、java.seモジュールにjava.baseモジュールへの推移的な依存関係は含まれていませんでした。すべてのモジュールに暗黙的な依存関係があるため、java.baseへの推移的な依存関係を持つモジュールはありませんでした。しかし、モジュール・インポート宣言はインポートする一連のパッケージを導出するため、java.baseモジュールを推移的に要求できるようになりました。