S-JIS[2008-07-05/2010-01-10] 変更履歴

Javaパッケージ


パッケージ

Javaの管理上、相互に関連する複数のクラスを 1つのパッケージとして扱う。[2003-07-06]
(ディレクトリー=パッケージの中に、ファイル=クラスが複数入っているイメージ)

自分がどのパッケージに属しているかは、それぞれのソースファイルの先頭に「package パッケージ名;」を記述することによって指定する。

package jp.hishidama.sample;

class Sample {
}

パッケージ宣言を書かない場合、「デフォルトパッケージ(無名パッケージ)」に属するものとして扱われる。[2008-07-05]

1つのソースファイル内には1つのパッケージ宣言しか書けない。[2008-07-05]
1つのソースファイル内には複数のクラスを書けるが、みな同じパッケージに属することになる。

ちなみに、パッケージ名の中のピリオド「.」の前後には、スペースや改行を入れても大丈夫。[2010-01-10]


普通は「ソースファイルを管理するディレクトリー」と「パッケージ名」は対応させる。

C:\workspace\sample\src>tree /f
C:.
└─jp
    └─hishidama
        └─sample
                Sample.java

が、本当は、ソースファイル自身がパッケージ構成と対応している必要は特に無い。[/2003-09-07]

ただし、クラスファイルの場所はパッケージ構成と対応している必要がある
コンパイルオプションで-dを指定 すると、パッケージ名に応じた場所(階層)にclassファイルが作られる。

パッケージの下(右)の部分をサブパッケージと呼ぶ。(ディレクトリー構造でサブディレクトリーというのと同じ)[2008-09-13]
例えば、パッケージjavaに対するawt, lang, util等がサブパッケージ。
例えば、パッケージjava.awtに対するimageがサブパッケージ。


なお、パッケージ名は全世界でユニークとなるように付けるものらしい
よくあるのが、URLを利用して、インターネットドメイン名を逆にして付ける方法。
例えばfoo.co.jpというドメインの会社の場合だと、jp.co.foo.project1といった感じ。


import

Javaでは、例えばjava.util.Listjava.util.ArrayListを使う場合は (限定名(FQN)を使って)以下のように書く。

class ImportSample {
	private java.util.List list = new java.util.ArrayList();
}

しかし毎回これを書くとソースが長くなってしまうので、「import」を使ってクラスを指定することにより、パッケージを省略して単純名だけで書けるようになる。[2003-07-06]

import java.util.List;
import java.util.ArrayList;

class ImportSample {
	private List list = new ArrayList();
}

※パッケージ名の中のピリオド「.」の前後には、スペースや改行を入れても大丈夫。[2010-01-10]

備考: ただし、自分と同じパッケージに属しているクラスに関しては、import宣言しなくても単純名でアクセスできる。[2010-01-10]

package sample1;

class A {
}
package sample1;

class B {
	private A a = new A();	//単純名でsample1.Aを指定できる。
}

トップレベルのクラス(ソースファイル内の直下に書かれているクラス)の可視性は、publicpackage private(何も書かない)しか無い。
パッケージプライベートだと同一パッケージにあるクラスにはアクセスできるし、publicだとどこからでもアクセスできるので、自分と同じパッケージのクラスには必ずアクセスできることになる。


異なるパッケージだがクラス名が同一の場合、2つ共を同時にインポートすることは出来ない。[2008-07-05]
例えばjava.util.Datejava.sql.Dateを同時に使う場合、

import java.util.Date;
import java.sql.Date;	←コンパイルエラーになる

importはどちらか片方だけにして、他方は限定名で指定する必要がある。

import java.util.Date;

	Date dt = 〜;		//java.util.Date(FQCN指定でない場合、importしたクラスとして扱われる)
	java.sql.Date sql = 〜;	//java.sql.Date
	java.util.Date d2 = 〜;	//java.util.Date(importしていてもFQCNで指定可能)

どうでもいいが、全く同じimport文を複数書いてもエラーにはならない。[2008-09-13]

import java.util.Date;
import java.util.Date;	//Eclipse3.2では「一度も使用されていません」という警告になるけどねー

また、同一パッケージに属するクラスはまとめてインポートすることが出来る。

import java.util.*;	//java.utilパッケージ直下のクラスを全てインポート

これは「オンデマンドのインポート宣言」と呼ばれるもの。[2008-09-13]
すなわち、ソース中で必要とされたクラスがこの中から探される。(つまり、この中のクラス全てがインポートされるわけではない。「*」は「全て」という意味ではなく、「必要に応じて」)

importでは、「*」でインポートするよりも個別にクラスを書く方が推奨されている。(どのクラスを使っているのかが明示的になる(はっきりする)ので)

また、複数のimportで「*」を使っていて同一クラス名が在る場合、そのクラスを単純名で使うことは出来ない。(「あいまいである」というコンパイルエラーになる。先にimportした方が使われるというわけではない)
例えばJDK1.4で以下のような書き方をしていた場合、

import java.util.*;
import javax.jms.*;

class QueueSample {
	private Queue q = null;	//javax.jmsのQueueクラス
}

JDK1.5でコンパイルすると、JDK1.5でjava.util.Queueが導入されたので、単純名「Queue」だけではどちらのものか判断できなくなり、コンパイルエラーになってしまう。
この場合、個別のクラス名のimport文を入れてやれば解決できる。

import javax.jms.Queue;
import java.util.*;
import javax.jms.*;
//import javax.jms.Queue;	入れる場所は、java.util.*やjavax.jms.*のimport文の前でも後でもいい

class QueueSample {
	private Queue q = null;	//javax.jmsのQueueクラス
}

内部クラスもアクセス可能(public)であればインポートすることが出来る。[2008-07-05]

package jp.hishidama.sample1;

public class Root {
	public class Inner {
	}
}
package jp.hishidama.sample2;

import jp.hishidama.sample1.Root.Inner;

class Sample2 {
	Inner obj;
}

デフォルトパッケージのクラス(package指定の無いクラス)はimportすることが出来ない。[2010-01-10]

public class A {
}
package sample;

import A;		←コンパイルエラーになる

class B {
	void test() {
		A a = new A();
	}
}

JavaFAQにはデフォルトパッケージのクラスをimportする例が載っているが、JDK1.3以前のjavacは出来たらしい。
が、これはバグで、出来ないのが正しい動作らしく、JDK1.4以降のjavacではコンパイルエラーになる。(バージョンに1.3以前を指定したとしても)
(参考: 人力検索はてな

Java言語仕様7.5.1章によると、インポート宣言できるのはクラスやインターフェースの完全限定名。すなわち「名前 . 識別子」という形式。
したがってデフォルトパッケージは(ピリオドより前の部分が無いから)指定できない、という事らしい。
Java言語仕様第3版では完全限定名でなく正準名に改められたようだが、ピリオドが必要な形式であることに変わりは無い)

自分もデフォルトパッケージに属している(package宣言をしていない)なら、「同一パッケージのクラスにはimportを書かなくてもアクセスできる」というルールにより、ソース中にそのまま書けばアクセスできる。
(デフォルトパッケージ同士も、同一パッケージ扱いされるから)

class C {
	void test() {
		A a = new A();
	}
}

でも、別パッケージからでも、リフレクションではアクセスできるんだそうだ。(参考: 人力検索はてな
まぁ、importはコンパイル時のクラスの特定に使われるのであって、リフレクションは実行時だからどこから呼ばれようと関係ないわなぁ。

package sample;

class B {
	void test() throws Exception {
		Object a = Class.forName("A").newInstance();
	}
}

importは よくC言語の#includeに例えられるようだが、どちらかというとextern宣言に近いと思う。[2003-07-06]
#includeは読み込んだソースをそのままその位置に展開して一緒にコンパイルするプリプロセッサの機能だが、externは「その関数を使うぞ」という宣言に過ぎず、その関数のコンパイルは別に行う。
importも「他でコンパイルされたクラスを使うぞ」と宣言ではないか?

もっとも、実際はコンパイルで使う(クラスのメソッドやフィールドを見る)為にそのクラスのバイトコードから定義を読み込んでいるのではないかと思うので、そういう意味では単なる宣言(extern)ではない。


Java標準パッケージ

Javaのライブラリーとして、様々なクラス(パッケージ)が用意されている。[/2008-07-05]
このうち、java.langパッケージに属しているクラスは、明示的にimportしなくても使用することが出来る。

主要なJavaパッケージ
パッケージ名 用途 備考 更新日
java.lang Javaの基本機能 特に指定しなくても、暗黙にimportされる。
Java言語仕様第3版7.5.5章『自動インポート』
2008-09-13
java.util ユーティリティー(リスト操作・時計操作…)    
java.io
java.nio
入出力 ファイルI/O  
java.math 算術演算    
java.net ネットワーク処理    
java.awt
javax.swing
グラフィカルユーザーインターフェース swing  
java.applet アプレット作成 Javaアプレット  

staticインポート

JDK1.5から、static importという構文が追加された。[2007-03-26]
→Sunのstaticのインポート
Java言語仕様第3版7.5.4章『オンデマンドのstaticインポート宣言』

これは、staticなメンバー(例えばstatic final(定数)のフィールド)をクラス名の指定無しに使えるようにするもの。

import static パッケージ.クラス.メンバー;
import static パッケージ.クラス.*;
import static java.lang.Math.PI;

public class StaticImport {
	public static void main(String[] args) {
		System.out.println(PI);
	}
}

インターフェース定数を定義してそれをimplementsすれば似たような事が出来るが、インターフェースは本来そういう風に使うものではないらしい。
それに そのやり方だと、全ての定数がpublicになってしまうので、本来公開したくない定数も公開されてしまう。(Javadocにまで出てしまう)[2008-05-02]


staticフィールド(メンバー変数)だけでなく、staticなメソッドもimportできる。[2007-09-11]
つまり、普通は他のクラスのstaticメソッドは「他クラス.method()」としなければ呼べないが、staticインポートしておけばクラスの指定無し、すなわち「method()」だけで呼ぶことが出来るようになる。

staticメソッドをオンデマンド(「*」)指定でインポートする場合、同名メソッド(すなわちオーバーロードされたメソッド)があれば、それら全てがインポートされる。[2008-09-13]

JUnit4でこの方法が使われている。


単純名・限定名・正準名

クラス名を指定するには、単純名限定名という方法がある。[2008-09-13]

単純名(simple name)は、単一の識別子で表す。例えば「String」とか「Date」とか。
限定名(qualified name)は、ピリオドを付けて必ず一意に定まるようにした表し方。例えば「java.lang.String」とか「java.util.Date」「java.sql.Date」とか。

パッケージ名を付けて指定したクラス名の書き方のことを、完全修飾クラス名(Fully Qualified Class Name:FQCN)あるいは完全限定名(fully qualified name:FQN)と呼ぶ。

また、正準名canonical name)という表現もある。基本的には限定名と同じだが、importで指定できるのは限定名でなく正準名。
例えば以下のようなクラスがあるとき、

package pack;

class Outer1 {
	class Inner {
	}
}

class Outer2 extends Outer1 {
}

pack.Outer1.Inner」と「pack.Outer2.Inner」はどちらも完全限定名であり、同じクラス(Outer1内のInner)を指す。
しかし正準名は「pack.Outer1.Inner」のみ(Innerが定義されているのはOuter1内だから)。


Java目次へ戻る / 新機能へ戻る / 技術メモへ戻る
メールの送信先:ひしだま