M.Hiroi's Home Page

続・お気楽 Java プログラミング入門

パッケージ


Copyright (C) 2016-2021 Makoto Hiroi
All rights reserved.

はじめに

プログラムを作っていると、以前作った関数と同じ処理が必要になる場合があります。いちばんてっとり早い方法はソースファイルからその関数をコピーすることですが、賢明な方法とはいえません。このような場合、自分で作成した関数をライブラリとしてまとめておくと便利です。ライブラリの作成で問題になるのが「名前の衝突」です。複数のライブラリを使うときに、同じ名前の関数や変数が存在すると、そのライブラリは正常に動作しないでしょう。この問題は「パッケージ (package)」を使うと解決することができます。

●パッケージの基本的な使い方

Java のパッケージはディレクトリで管理します。ディレクトリを作成して、そこに class ファイルを格納すれば、それをパッケージとして使用することができます。このとき、ディレクトリ名がパッケージ名になります。ディレクトリ名と class ファイル名は異なっていてもかまいません。ディレクトリの中に複数の class ファイルがある場合、それらをまとめたものが一つのパッケージになります。

簡単な例を示しましょう。次のリストを見てください。

リスト : foo\Foo.java

package foo;

public class Foo {
  public static int a = 10;
  public static void test() {
    System.out.println("package foo");
  }
}
リスト : bar\Bar.java

package bar;

public class Bar {
  public static int a = 20;
  public static void test() {
    System.out.println("package bar");
  }
}

カレントディレクトリにディレクトリ foo を作成し、その中にファイル Foo.java を格納します。同様に、ディレクトリ bar を作成して、ファイル Bar.java を格納します。ファイルの先頭には package 文でパッケージ名を記述します。外部に公開 (export) するクラスは public で宣言します。このとき、ファイル名は public 宣言したクラスの名前と同じにしてください。つまり、ファイル内で定義できる public なクラスは一つだけになります。

パッケージ内に定義されているクラスやメソッドなどの名前は、次の方法でアクセスすることができます。

パッケージ名.名前

パッケージ名が長いとプログラムを記述するのが大変です。パッケージ名を省略して名前だけでアクセスできると便利ですね。このために用意されている構文が import です。

  1. import パッケージ名.名前;
  2. import パッケージ名.*;

2 は「オンデマンドのインポート宣言」と呼ばれる方法で、そのパッケージからプログラムで必要となる名前をインポートします。import では、2 の方法よりも個別に名前を書く 1 の方法が推奨されているようです。

環境変数 CLASSPATH を設定していない場合、もしくは CLASSPARH にカレントディレクトリを追加している場合、クラス Foo と Bar は次のように指定します。

import foo.Foo;
import bar.Bar;

Java はクラスを探すとき、CLASSPATH が設定されている場合は、そのディレクトリから探します。CLASSPATH が設定されていなければ、カレントディレクトリを探します。見つからない場合は Java の標準ライブラリから探します。CLASSPATH を設定すると、カレントディレクトリを探索しないので、CLASSPATH にカレントディレクトリを追加するか、コマンド javac のオプション -classpath でカレントディレクトリ ( . ) を指定してください。

それでは実行してみましょう。

リスト : パッケージの使用例 (sample160.java)

import foo.Foo;
import bar.Bar;

public class sample160 {
  public static void main(String[] args) {
    Foo.test();
    System.out.println(Foo.a);
    Bar.test();
    System.out.println(Bar.a);
  }
}
$ javac foo/Foo.java
$ ls foo
Foo.class  Foo.java
$ javac bar/Bar.java
$ ls bar
Bar.class  Bar.java
$ javac sample160.java
$ java sample160
package foo
10
package bar
20

最初に import で foo.Foo と bar.Bar をインポートします。これでパッケージに含まれる Foo, Bar にアクセスすることができます。Foo.test() を実行すれば、package foo と表示され、Bar.a の値を表示すると 20 になります。このように、パッケージを使うことで名前の衝突を回避することができます。

foo と bar を一つのディレクトリに格納することもできます。次の図を見てください。

./
    baz/
        foo/
            Foo.java
        bar/
            Bar.java

ディレクトリ baz の中にディレクトリ foo と bar があり、その中にソースファイル Foo.java と Bar.java があります。この場合、Foo.java と Bar.java の package 文は次のように宣言します。

package baz.foo;
package baz.bar;

sample160.java の import 文は次のように指定します。

import baz.foo.Foo;
import baz.bar.Bar;

package 文と import 文のパス区切り記号にはドット ( . ) を使います。これで今までと同様にクラス Foo, Bar を使用することができます。

ところで、パッケージに必要なのは class ファイルで、ソースファイルは別のディレクトリにあってもかまいません。たとえば、カレントディレクトリにソースファイル Foo.java, Bar.java, sample160.java があり、Foo.class と Bar.class を package 文で指定したディレクトリに配置する場合、javac のオプション -d を使うと便利です。

-d ディレクトリ

-d オプションはクラスファイルの出力先ディレクトリを設定します。javac は必要に応じてディレクトリを作成し、パッケージ名に対応したサブディレクトリに class ファイルを配置します。たとえば、Foo.java, Bar.java をコンパイルするとき -d . を指定すると、カレントディレクトリにサブディレクトリ baz を作成し、そのサブディレクトリに foo と bar を作成し、そこに class ファイルを配置します。

それでは実際に試してみましょう。

$ ls
Bar.java  Foo.java  sample160.java
$ javac -d . Foo.java
$ ls
Bar.java  Foo.java  baz  sample160.java
$ ls baz
foo
$ ls baz/foo
Foo.class
$ javac -d . Bar.java
$ ls baz
bar  foo
$ ls baz/bar
Bar.class
$ javac sample160.java
$ java sample160
package foo
10
package bar
20

●Java Archive

Java はコマンド jar を使って、パッケージ内にある複数の class ファイルを一つのファイルにまとめて取り扱うことができます。このファイルを Java Archive とか JAR ファイルといいます。

jar で JAR ファイルを作成するときは次のオプションを指定します。

jar cf ファイル名.jar パッケージ

jar のオプションは UNIX 系 OS のコマンド tar と同じです。オプションに v を付けるとメッセージが表示されます。たとえば、パッケージ baz を JAR ファイルにまとめると次のようになります。

$ jar cvf baz.jar baz
マニフェストが追加されました
baz/を追加中です(入=0)(出=0)(0%格納されました)
baz/bar/を追加中です(入=0)(出=0)(0%格納されました)
baz/bar/Bar.classを追加中です(入=475)(出=320)(32%収縮されました)
baz/foo/を追加中です(入=0)(出=0)(0%格納されました)
baz/foo/Foo.classを追加中です(入=475)(出=320)(32%収縮されました)

これで JAR ファイル baz.jar が生成されます。JAR ファイルの内容は次のコマンドで閲覧することができます。

jar tf ファイル名
$ jar tf baz.jar
META-INF/
META-INF/MANIFEST.MF
baz/
baz/bar/
baz/bar/Bar.class
baz/foo/
baz/foo/Foo.class

●JAR ファイルの使い方

JAR ファイルを利用する場合は環境変数 CLASSPAT かコマンドのオプション -classpath (-cp) でJAR ファイル名を指定します。

$ javac -cp baz.jar sample160.java
$ java -cp baz.jar:. sample160
package foo
10
package bar
20

コマンド java で sample160 を実行する場合、CLASSPATH を設定するとカレントディレクトリの探索を行わないので、sample160 を見つけることができなくなります。この場合、CLASSPATH にカレントディレクトリ ( . ) を追加してください。パスの区切り記号は、Unix 系の OS ではコロン ( : ) を、Windows ではセミコロン ( ; ) を使います。これで複数のパスや JAR ファイルを指定することができます。探索の順序は左から右になります。

マニフェストファイルで main() が定義されているクラスを指定すると、実行可能な JAR ファイルを作成することができます。名前は何でもかまいません。次に示すように Main-Class: の後ろに main() が定義されているクラスを指定するだけです。

リスト : Manifest.txt

Main-Class: sample160

jar コマンドではオプション m と引数に Manifest.txt を指定します。

$ jar cvfm sample.jar Manifest.txt sample160.class baz
マニフェストが追加されました
sample160.classを追加中です(入=496)(出=348)(29%収縮されました)
baz/を追加中です(入=0)(出=0)(0%格納されました)
baz/bar/を追加中です(入=0)(出=0)(0%格納されました)
baz/bar/Bar.classを追加中です(入=475)(出=320)(32%収縮されました)
baz/foo/を追加中です(入=0)(出=0)(0%格納されました)
baz/foo/Foo.classを追加中です(入=475)(出=320)(32%収縮されました)

$ jar tf sample.jar
META-INF/
META-INF/MANIFEST.MF
sample160.class
baz/
baz/bar/
baz/bar/Bar.class
baz/foo/
baz/foo/Foo.class

これで、Manifest.txt の内容が JAR ファイル内の MANIFEST.MF にコピーされます。なお、baz.jar には main() を定義しているクラスがないので、引数に sample160.class も指定する必要があります。

JAR ファイルを実行するにはオプション -jar を使います。それでは実行してみましょう。

$ java -jar sample.jar
package foo
10
package bar
20

●import static

import 文で static を指定すると、クラス名を指定せずにスタティックメソッドやスタティックなフィールド変数にアクセスできるようになります。

import static パッケージ名.クラス名.staticな変数名またはメソッド名;
import static パッケージ名.クラス名.*;

たとえば、クラス java.lang.Math には便利な数学関数が定義されていますが、static import すれば Math を省略してアクセスすることができます。

リスト : import static の使用例

import static java.lang.Math.sqrt;
import static java.lang.Math.PI;

public class sample161 {
  public static void main(String[] args) {
    System.out.println(sqrt(2.0));
    System.out.println(PI);
  }
}
$ javac sample161.java
$ java sample161
1.4142135623730951
3.141592653589793

初版 2016 年 12 月 10 日
改訂 2021 年 2 月 21 日