Java/文法/パッケージ

Javaのパッケージは、クラスを管理しやすくするためにあります。 パッケージを用いることで名前が同じクラスを作ることができるようになります。 C++経験がある人は、C++にある名前空間(namespace)のようなものだと思って下さい。
なお、WikipediaにはJavaのパッケージの記事があります。
Javaでのパッケージ宣言は、Javaソースコードの先頭で以下のように記述します。
パッケージ名は、パッケージ宣言するときにドット(.)で区切ることでサブパッケージをいくつでも作ることができます。 では実際にJavaソースコードでパッケージ宣言をしてみましょう。
このJavaソースファイルをPackageDemo.javaという名前で保存します。 Javaソースファイルを保存するとき、保存するディレクトリを指定して下さい。 (ここではWindowsを例に説明します。LinuxやUNIXなどのOSではバックスラッシュ(\)をスラッシュ(/)に置き換えてルートディレクトリも変えて下さい。)
ここでは「ディレクトリ」という言葉を使っていますが、これはWindowsでは「フォルダ」と同じ意味です。
例えば、Javaプログラムを置くディレクトリが C:\wikibooks\java\src であるとします。 package宣言で指定したパッケージ名はorg.wikibooks.ja.newpackageですので、 パッケージのディレクトリはorg\wikibooks\ja\newpackageとなります。実際にはパッケージ名とディレクトリ名を一致させる必要はありませんが、Javaでは、わかりやすさのためにパッケージに合わせてディレクトリを作成してそこにJavaソースファイルを置くことが慣習になっています。
すると絶対パスでPackage.javaを置くディレクトリは
となります。
このJavaソースファイルをコマンドラインで実際にコンパイルするには以下のようにします。
すると、クラスファイルがディレクトリ C:\wikibooks\java\src\org\wikibooks\ja\newpackage にできます。 このクラスを実際に実行するには以下のようにします。
これで「パッケージテスト」という文字が表示されます。 ここでjavaコマンドのオプションに-classpathを指定していることに気を付けて下さい。これはJavaクラスファイルがカレントディレクトリC:\wikibooks\java\srcから見て相対的にorg\wikibooks\ja\newpackageにないときに、明示的にクラスファイルがある相対パスを指定しています。カレントディレクトリがC:\wikibooks\java\srcでないときにこのオプションを指定しないと、javaコマンドはクラスファイルを見つけることができず、エラーとなってしまいます。 カレントディレクトリがC:\wikibooks\java\srcの場合は、以下のように指定して下さい。
-classpathオプションは短縮して-cpと書くこともできます。 環境変数CLASSPATHを使用する方法がありますが、Javaの特性を損なう環境依存を引き起こすため、お勧めはしません。なるべく-classpathオプションを使用しましょう。 これでパッケージの使用方法を説明しました。 しかし、このままではパッケージの利点がわかりにくいかもしれません。 そこで、もう一つJavaソースファイルを作ってみましょう。
今度は、異なるディレクトリにファイルを置きます。 ここでは、C:\wikibooks\java\src\org\wikibooks\ja\newpackage に subとsub2という名前のディレクトリを作成して下さい。sub という名前のディレクトリを作成します。そのsubというディレクトリに、新たに、PackageDemo.javaというJavaソースファイルを新規作成して下さい。先ほど作成したJavaソースファイルと全く同じ名前ですが、違うディレクトリにファイルを置いているため、名前衝突が起こりません。もう一つ、同じディレクトリにOtherClass.javaというJavaソースファイルを新規作成して下さい。sub2というディレクトリには OtherPackageClass.javaという名前のJavaソースファイルを新規作成して下さい。 すると、以下のようなディレクトリ構成になります。
subディレクトリで作成したPackageDemo.javaは以下のように書いて下さい。
OtherClass.javaは以下のように書いて下さい。
OtherPackageClass.javaは以下のように書いて下さい。
これらのJavaソースファイルは以下のようにしてコンパイルします。
ここで、*(アスタリスク)はワイルドカードを意味します。ワイルドカードとは、任意の文字列のことであり、喩えるならトランプのポーカーでいうジョーカーの札にようなもにです。この例ではsubディレクトリとsub2.javaで終わるJavaソースファイルをまとめてコンパイルするということを意味します。 すると、三つのクラスファイルがディレクトリ C:\wikibooks\java\src\org\wikibooks\ja\newpackage\sub と C:\wikibooks\java\src\org\wikibooks\ja\newpackage\sub2 にできます。 このクラスを実際に実行するには以下のようにします。
すると、以下のように表示されます。
この例では、subパッケージにあるPackageDemoクラスが、同じパッケージ内のOtherClassクラスを呼び出しています。 つぎに、OtherClassを実行するときに、OtherClassは他のパッケージsub2にあるOtherPackageClassにアクセスしています。OtherPackageClassはsubとは異なるパッケージ、sub2という名前のパッケージであるため、import宣言を用いて目地的にパッケージを指定する必要があります。次にimport宣言について説明します。
subというパッケージにあるOtherClassは異なるパッケージsub2にあるOtherPackageClassにアクセスしています。そのために、明示的にパッケージ名を指定しなければOtherPackageClassを使用することはできません。 OtherClass.javaのソースコードを見てみましょう。OtherClass.javaにはこのようなimport宣言があります。
これはorg.wikibooks.ja.newpackage.sub2パッケージにあるOtherPackageClassクラスを参照していることを意味します。C言語の#include宣言に似ていますが、若干違います。import宣言を使わなくてもOtherPackageClassを呼び出す事は可能です。そこがC言語とは大きく異なります。その方法は以下のようにします。
OtherClass.javaを以下のように書き換えます。
これは以前に書いたOtherClass.javaと等価です。どちらも同じですが、こちらはソースコードが読みづらいですね。これは好みの問題ともいえますが、急いでいるときにこの手を使うと良いでしょう。好みの問題以外にも、どうしてもこの完全修飾名をつかわなければならないケースがあります。例えば、PackageDemoクラスのように、パッケージ名は異なるけれども同じ名前のクラスがありますね。こういうときに、この完全修飾名は大いに有効な手段として使うことができます。import宣言で宣言できるクラスは、同じ名前のクラスであってはいけないのです。ふたつのPackageFDemoクラスを一つのJavaソースファイル内で同時にimport宣言するとエラーになってしまいます。そのため。片方、または両方をこのように長い完全修飾名で呼び出すことで、クラス名の衝突を回避することができます。
このような名前が衝突する恐れがある組み合わせはJava標準APIにも実際に存在します。その一例がjava.util.Dateとjava.sql.Dateです。これらのクラスは同時にimportすることはできません。完全修飾名を使う必要があります。 ただし、通常はこんなことをする必要はありません。委譲による「ラッパークラス」の自作や、デザインパターンの一種「Facade パターン」などの手法を使って、別のクラスを作成することで名前の衝突を防ぐことが可能です。java.util.Dateとjava.sql.Dateについては、よほどのことでもない限り、同時に使うことは滅多にないでしょう。頻繁に完全修飾名を使うようになることがあるなら、クラス名のネーミングや設計に問題があると思った方が良いでしょう。デザインパターンについてよく勉強すれば、こういうことをしなくても済むようになります。