
Java入門|異なるパッケージのクラスを使う
パッケージをまたいでクラスを使えるようになると、Javaの設計はぐっと実践的になる
ここまでで、クラスをファイルごとに分けることや、同じパッケージの中にクラスをまとめることを見てきました。ここまでの段階では、関連するクラスをひとつのグループとして整理しながら使う流れが中心でしたね。
けれども、実際のJava開発では、いつも同じパッケージのクラスだけを使うとは限りません。別の機能を担当するパッケージにあるクラスを利用したり、ほかの人が作成したクラスを自分のプログラムから呼び出したりする場面がたくさんあります。大きなプログラムになるほど、このような異なるパッケージどうしの連携はとても重要になります。
ただし、異なるパッケージのクラスは、同じパッケージのときのようにそのまま気軽に使えるわけではありません。Javaでは、どのクラスを外部に公開するのか、どのパッケージのクラスを使おうとしているのかを、きちんと明示する必要があります。
今回は、ドラゴンボールの世界をイメージしたクラス名を使いながら、異なるパッケージのクラスを使うときに何が問題になり、どうすれば正しく使えるのかを、順を追ってしっかり整理していきましょう。
異なるパッケージに分けると何が起こるのか
Javaでは、別々のファイルに書かれたクラスを、別々のパッケージに所属させることができます。
これは、大規模な開発ではとても自然なことです。
たとえば、戦士そのものを表すクラスを pa パッケージに入れ、実行用のクラスを pb パッケージに入れる、といった分け方ができます。こうすることで、クラスの役割ごとに所属先を整理できます。
ただし、ここで最初に押さえておきたいのは、異なるパッケージのクラスは、そのままでは利用できないという点です。
同じパッケージにいるときの感覚でクラス名だけを書いてしまうと、Javaはそのクラスを見つけられないことがあります。
なぜそのままでは使えないのか
Javaでは、クラス名だけを書いた場合、まずは同じパッケージの中にあるクラスとして探されます。
そのため、pb パッケージにいる Sample4 クラスの中で単に Fighter と書いても、Javaは pb の中にある Fighter クラスを探そうとします。
ところが、使いたい Fighter クラスが pa や pc など別のパッケージにある場合、pb の中にはそのクラスがありません。
その結果、コンパイル時に「そのクラスは見つかりません」という状態になります。
ここが、同じパッケージのときとの大きな違いです。
まずは失敗する例を見てみよう
最初に、異なるパッケージにあるクラスを、そのままの感覚で使おうとして失敗する例を見てみます。
ここでは、Sample4 クラスを pb パッケージに含め、別パッケージにある Fighter クラスを使おうとします。
ファイル名:Sample4.java(pbディレクトリに配置)
このコードは、一見するととてもシンプルです。
オブジェクトを作って、show メソッドを呼び出しているだけに見えます。
けれども、この書き方では正しくコンパイルできません。
package pb;
class Sample4 {
public static void main(String[] args) {
Fighter fighter1 = new Fighter();
fighter1.show();
}
}ファイル名:Fighter.java(pcディレクトリに配置)
package pc;
// 戦士クラス
public class Fighter {
private String name;
private int power;
public Fighter() {
name = "未設定";
power = 0;
System.out.println("戦士を作成しました。");
}
public void setFighter(String n, int p) {
name = n;
power = p;
System.out.println("戦士名を" + name + "、戦闘力を" + power + "にしました。");
}
public void show() {
System.out.println("戦士名は" + name + "です。");
System.out.println("戦闘力は" + power + "です。");
}
}コンパイルのしかた
コンパイルは、作業中ディレクトリから行います。
たとえば作業中ディレクトリが C:\Java\13 なら、次のように入力します。
C:\Java\13>javac pb\Sample4.java
pb\Sample4.java:5: エラー: シンボルを見つけられません
Fighter fighter1 = new Fighter();
^
シンボル: クラス Fighter
場所: クラス Sample4
pb\Sample4.java:5: エラー: シンボルを見つけられません
Fighter fighter1 = new Fighter();
^
シンボル: クラス Fighter
場所: クラス Sample4
エラー2個Sample4.java がコンパイルできない理由
Sample4.java が pb パッケージに属しているなら、この中で単に Fighter と書いたとき、Javaはまず pb パッケージの中の Fighter クラス を探します。
しかし、使いたい Fighter クラスが別のパッケージにあるなら、pb の中にはその名前のクラスは存在しません。
そのため、Javaは Fighter を解決できず、コンパイルエラーになります。
この流れを表で整理すると、こうなります。
| 書き方 | Javaが探す場所 | 結果 |
|---|---|---|
| Fighter | まず同じパッケージ pb | 見つからなければエラー |
| pc.Fighter | pc パッケージの Fighter | 正しく指定できる |
つまり、異なるパッケージのクラスを使うときは、どのパッケージのクラスなのかを明示する必要があるのです。
1つのファイルを異なるパッケージには分けられない
ここであわせて覚えておきたいことがあります。
それは、1つのソースファイルの中に異なるパッケージのクラスを混在させることはできないということです。
理由はシンプルで、1つのソースファイルには package 文を1つしか書けないからです。
つまり、そのファイルに書かれたクラスは、基本的にその package 文で指定した1つのパッケージに属します。
このルールを表にすると次のようになります。
| 項目 | 内容 |
|---|---|
| package 文の数 | 1つのソースファイルに1つだけ |
| 1つのファイルに含まれるクラス | 同じパッケージに属する |
| 異なるパッケージにしたい場合 | ファイルを分ける必要がある |
このため、異なるパッケージに所属させたいクラスは、必ず別々のファイルに記述します。
異なるパッケージのクラスを使うための2つの条件
異なるパッケージのクラスを正しく使うには、次の2つが必要です。
| 条件 | 内容 |
|---|---|
| 1 | 利用されるクラスを public にする |
| 2 | 利用する側で パッケージ名.クラス名 と書く |
この2つがそろってはじめて、別パッケージのクラスを安全に利用できます。
利用されるクラスを public にする
クラスの先頭に public をつけると、そのクラスは別のパッケージからも利用できるクラスになります。
反対に、public をつけないクラスは、同じパッケージの中からしか使えません。
そのため、外部のパッケージから使ってほしいクラスには public が必要です。
パッケージ名を含めて書く
異なるパッケージのクラスを使う側では、ただ Fighter と書くのではなく、どのパッケージにある Fighter なのかを示す必要があります。
たとえば pc パッケージにある Fighter クラスなら、次のように書きます。
pc.Fighterこうすることで、Javaは「pc パッケージの中の Fighter クラスを使うのだな」と正しく判断できます。
正しく使う例を見てみよう
では、異なるパッケージのクラスを正しく使う側の例を見てみます。
ここでは、pb パッケージに属する Sample5 クラスから、pc パッケージの Fighter クラスを使うことにします。
ファイル名:Sample5.java
package pb;
class Sample5 {
public static void main(String[] args) {
pc.Fighter fighter1 = new pc.Fighter();
fighter1.show();
}
}このコードでは、クラス名の前に pc. をつけています。
これによって、pb パッケージの中から、pc パッケージにある Fighter クラスを明示的に利用しています。
Sample5.java が正しく動く理由
Sample5.java が正しく動くのは、使いたいクラスの場所がはっきり示されているからです。
pc.Fighter fighter1 = new pc.Fighter();この1行の中では、型としての Fighter も、new で生成する Fighter も、どちらも pc パッケージのものだと指定しています。
もし pc パッケージ側の Fighter クラスが public であれば、pb パッケージからでも使えます。
そのため、異なるパッケージにあるクラスでも、条件を満たせば問題なく利用できます。
Sample4 と Sample5 の違いを整理する
2つのコードの違いを並べると、とてもわかりやすくなります。
| 項目 | Sample4 | Sample5 |
|---|---|---|
| クラスの使い方 | Fighter | pc.Fighter |
| 利用先の明示 | していない | している |
| コンパイル | 失敗する | 成功する |
| 理由 | 同じパッケージ内のクラスとして探してしまう | 別パッケージのクラスを明示している |
この違いこそが、異なるパッケージのクラスを使う学習でいちばん大切なポイントです。
実行結果のイメージ
C:\Java\13>java pb.Sample5pc パッケージの Fighter クラスが、たとえば次のような動きをするように作られていたとします。
- コンストラクタで 「戦士を呼び出しました。」 と表示する
- show メソッドで 「戦士名は未設定です。」 と表示する
- show メソッドで「 戦闘力は0です。」 と表示する
その場合、Sample5 を実行したときの表示は、たとえば次のようになります。
戦士を作成しました。
戦士名は未設定です。
戦闘力は0です。表示内容そのものより大切なのは、異なるパッケージにあるクラスでも、正しい書き方をすればふつうにメソッドを呼び出せるという点です。
コンパイルと実行の考え方
ここでは Sample5 を実行する場面を考えてみます。
作業中ディレクトリの下に pb フォルダと pc フォルダがあり、それぞれのパッケージのファイルが置かれているとします。
イメージはこんな形です。

この状態で、作業中ディレクトリからコンパイルや実行を行います。
コンパイルの例は次のようになります。
PS C:\Java\13>javac pb\Sample5.java実行は次のようになります。
PS C:\Java\13>java pb.Sample5ここでも大事なのは、実行時にはパッケージ名.クラス名で指定することです。
Sample5 は pb パッケージに属しているので、java Sample5 ではなく java pb.Sample5 と書きます。
public をつけたクラスのルール
異なるパッケージのクラス利用では、public の意味がとても重要です。
ここでクラスにつける public の基本を整理しておきましょう。
| クラスにつける指定 | 意味 |
|---|---|
| 無指定 | 同じパッケージからのみ使える |
| public | 異なるパッケージからも使える |
つまり、ほかのパッケージから利用してほしいクラスには public が必要です。
さらに、public クラスには大事なルールがあります。
| ルール | 内容 |
|---|---|
| public クラスは1ファイルに1つだけ | 複数は書けない |
| ファイル名は public クラス名と同じ | Fighter なら Fighter.java |
このルールはとても大切です。
たとえば public class Fighter と書いたなら、そのソースファイル名は Fighter.java にしなければなりません。
修飾子の意味もここで整理しておこう
このあたりで、修飾子の役割も頭の中で整理しておくと理解が深まります。
クラス・インターフェイスにつける修飾子
| 修飾子 | 意味 |
|---|---|
| 無指定 | 同じパッケージからのみ利用できる |
| public | 異なるパッケージからも利用できる |
メンバ・コンストラクタにつける修飾子
| 修飾子 | 意味 |
|---|---|
| private | 同じクラス内でのみアクセスできる |
| 無指定 | 同じパッケージからのみアクセスできる |
| protected | 同じパッケージ、または別パッケージのサブクラスからアクセスできる |
| public | すべてのクラスからアクセスできる |
今回の中心はクラスに対する public ですが、今後の学習ではフィールドやメソッドの公開範囲も大切になってきます。
パッケージ名で同名クラスを区別できる
Javaのパッケージが本当に便利なのは、同じクラス名でも別のクラスとして共存できるところです。
たとえば、
- pa.Fighter
- pc.Fighter
という2つがあれば、どちらも Fighter という名前ですが、所属するパッケージが違うので別のクラスとして扱われます。
これは大規模開発ではとても大切です。
別々の開発者が偶然同じクラス名を使っていたとしても、パッケージ名まで含めて区別できるからです。
表で整理するとこうなります。
| 記述 | 意味 |
|---|---|
| pa.Fighter | pa パッケージの Fighter クラス |
| pc.Fighter | pc パッケージの Fighter クラス |
クラス名だけが同じでも、パッケージ名が違えば別物です。
このような名前の区別のしくみを、名前空間と呼びます。
名前空間とは何か
名前空間という言葉は少し難しく感じるかもしれませんが、考え方はシンプルです。
名前がぶつからないように、クラス名をグループごとに管理する仕組みだと思えば大丈夫です。
たとえば Fighter という名前だけで管理すると、同名クラスが増えたときに混乱します。
でも pa.Fighter や pc.Fighter のように、パッケージ名も含めて管理すれば、同じ Fighter でも区別できます。
つまり、パッケージは単なるフォルダ分けではなく、クラス名を整理して衝突を防ぐ仕組みでもあるのです。
パッケージ名のつけ方
実際の開発では、パッケージ名は自由に決められますが、他人とかぶりにくくするために、組織のドメイン名を逆順にした形がよく使われます。
たとえば、組織のドメインが xxx.co.jp なら、次のような形が推奨されます。
jp.co.xxxこれを先頭にして、さらに機能ごとに続けていくイメージです。
jp.co.xxx.battle
jp.co.xxx.character
jp.co.xxx.trainingこうすると、世界中の多くの開発者がいても、名前がぶつかりにくくなります。
図で全体の流れを整理する

この図では、上側または左側にある Fighter クラスが、別パッケージのクラスとして配置されています。
Sample4 はそのクラスを単純なクラス名だけで使おうとしているため、途中でつながらず、利用できない様子が表されています。
一方、Sample5 では、利用されるクラスが public になっていて、さらに利用する側が pc.Fighter と明示しているため、正しくつながっています。
この違いを見ることで、異なるパッケージのクラス利用に必要な条件がひと目で理解できます。
学習の最初につまずきやすいところ
この内容では、次のような点でひっかかりやすいです。
| つまずきやすい点 | 理解のポイント |
|---|---|
| 別パッケージでもクラス名だけで使えると思ってしまう | 異なるパッケージでは場所を明示する必要がある |
| public の意味があいまい | public クラスは外部パッケージから利用できる |
| ファイル名のルールを忘れやすい | public クラス名とファイル名は同じにする |
| 同名クラスは使えないと思ってしまう | パッケージ名が違えば別クラスとして共存できる |
この4つが整理できると、パッケージの理解がかなり安定します。
ここまでで押さえたいこと
今回の内容で、特に大事なのは次の点です。
| 大事なポイント | 内容 |
|---|---|
| 異なるパッケージのクラスはそのままでは使えない | 同じパッケージ内のクラスとしては見つからないため |
| 利用されるクラスには public が必要 | 外部パッケージから利用可能にするため |
| 利用するときは パッケージ名.クラス名 と書く | どのクラスを使うか明示するため |
| パッケージ名が違えば同名クラスも区別できる | 名前空間として働くため |
この考え方が身につくと、Javaのパッケージ機能をかなり実践的に理解できるようになります。
次に import を学ぶと、今回の パッケージ名.クラス名 という書き方がさらに整理されて、コードをもっと読みやすく書けるようになります。ここまでの内容は、そのためのとても大切な土台です。
