Java入門|パッケージの基本

クラスの住所をはっきりさせると、Javaのプログラムはもっと整理しやすくなる

プログラムが小さいうちは、クラスの数もそれほど多くないので、どのクラスがどこにあるのかを意識しなくても作業を進めやすいものです。けれども、開発する機能が増えてくると、使うクラスの数もどんどん増えていきます。さらに、自分で作ったクラスだけでなく、ほかの人が作成したクラスも組み合わせて使う場面が増えてきます。

そうなると困りやすいのが、同じ名前のクラスが存在する場合です。たとえば、別々の人がそれぞれ Fighter という名前のクラスを作っていたら、どちらを使いたいのかがわかりにくくなってしまいます。こうした混乱を防ぐために、Javaではクラスをグループ分けして整理するしくみが用意されています。それがパッケージです。

パッケージは、クラスをしまっておくための整理棚のようなものです。どのクラスがどのまとまりに属しているかを明確にできるので、大きなプログラムでも見通しよく管理できます。ここでは、ドラゴンボールをイメージしたクラスを使いながら、パッケージの基本的な考え方と使い方を、ひとつずつやさしく見ていきましょう。

パッケージとは何か

Javaのパッケージは、クラスを分類して整理するためのしくみです。
イメージとしては、ファイルをそのまま机の上に並べるのではなく、名前のついたフォルダに分けて保存する感じに近いです。

たとえば、戦士に関するクラスを pa というパッケージにまとめておけば、そのクラスは pa というグループに属していることがはっきりします。こうしておくと、同じ名前のクラスが別の場所に存在していても、パッケージ名を含めて区別できます。

Javaでは、ソースファイルの先頭に次のように書くことで、そのファイル内のクラスをパッケージに含められます。

package パッケージ名;

この指定を書くと、そのソースファイルに書かれたクラスは、そのパッケージに属するクラスとして扱われます。

パッケージが必要になる理由

パッケージは、単に見た目を整えるためだけのものではありません。大きなプログラムを作るときには、とても大切な役割を持っています。

たとえば、クラスが増えてくると、次のようなことが起こりやすくなります。

状況パッケージがない場合パッケージがある場合
クラス数が多いどこに何があるか把握しにくい分類して整理しやすい
同名クラスがある区別しにくいパッケージ名で区別できる
複数人で開発するクラスの配置が混乱しやすいグループごとに分担しやすい
再利用したいまとまりが見えにくい機能ごとにまとめやすい

つまり、パッケージはクラスの住所を決める仕組みだと考えるとわかりやすいです。
住所があるからこそ、たくさんのクラスがあっても迷いにくくなるのです。

パッケージに含める書き方

クラスをパッケージに含めたいときは、ソースファイルの先頭に package 文を書きます。

今回は pa というパッケージ名を使って説明します。
このとき、ファイルの先頭には次のように書きます。

package pa;

この1行があることで、そのファイルに書かれたクラスは pa パッケージに属するクラスになります。

ここで大切なのは、package 文はファイルの先頭に書くことです。
途中に書くのではなく、最初に書いて、このファイルはどのパッケージに属しているのかをはっきり示します。

サンプルプログラムで見てみよう

ここでは、ドラゴンボールの世界をイメージして、戦士を表す Fighter クラスを使ってみます。
戦士名と戦闘力を持ち、それらを設定するための setFighter メソッドを用意します。

この1つのファイルの中に、Fighter クラスと Sample2 クラスを書き、どちらも pa パッケージに含めます。

ファイル名:Sample2.java

package pa;

// 戦士クラス
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 + "です。");
    }
}

class Sample2 {
    public static void main(String[] args) {
        Fighter fighter1 = new Fighter();
        fighter1.setFighter("悟空", 9000);
        fighter1.show();
    }
}

このプログラムの見どころ

このコードでは、ファイルの先頭に package pa; と書かれています。
そのため、このファイルの中にある Fighter クラスと Sample2 クラスは、どちらも pa パッケージに含まれます。

ポイントを整理すると、次のようになります。

要素役割
package pa;このファイル内のクラスを pa パッケージに含める
Fighter クラス戦士の情報を持つ
setFighter メソッド戦士名と戦闘力を設定する
show メソッド現在の情報を表示する
Sample2 クラスmain メソッドを持ち、処理を開始する

この例では、Sample2 クラスの main メソッドから Fighter クラスのオブジェクトを作り、情報を設定して表示しています。
同じパッケージに含まれているので、特別な書き方をしなくても、そのまま Fighter を利用できます。

実行結果

このプログラムを実行すると、次のような表示になります。

戦士を準備しました。
戦士名を悟空、戦闘力を9000に設定しました。
戦士名は悟空です。
戦闘力は9000です。

保存するときのフォルダ構成

パッケージを使うときは、ソースコードの書き方だけでなく、フォルダの配置も大切です。
pa というパッケージを使うなら、作業中のフォルダの下に pa という名前のフォルダを作成し、その中に Sample2.java を保存します。

たとえば、作業中のフォルダが 13 だとすると、次のような構成になります。

ここがパッケージ学習の最初の山場です。
package pa; と書いたなら、対応する pa フォルダの中に保存する必要があります。

コンパイルのしかた

コンパイルは、pa フォルダの中に入って行うのではなく、作業中のフォルダから行うのがポイントです。

たとえば、作業中のフォルダが C:\Java\13 なら、次のように入力します。

PS C:\Java\13>javac pa\Sample2.java

ここでは、ディレクトリ名とファイル名をあわせて指定しています。
つまり、pa フォルダの中にある Sample2.java をコンパイルしているわけです。

コンパイルが終わると、pa フォルダの中に class ファイルが作成されます。

このように、1つのソースファイルの中に複数のクラスを書いている場合は、それぞれに対応した class ファイルが作られます。

実行のしかた

実行するときも、作業中のフォルダから命令を入力します。
ただし、パッケージを使っているので、実行時にはパッケージ名.クラス名の形で指定します。

PS C:\Java\13>java pa.Sample2

ここで Sample2 だけではなく、pa.Sample2 と書くのが大事です。
これは、Sample2 クラスが pa パッケージに属しているからです。

この書き方によって、Javaは pa パッケージの中にある Sample2 クラスを見つけて実行できます。

パッケージを使うとコンパイルと実行が少し変わる

これまでの学習では、同じフォルダにソースファイルを置いて、クラス名をそのまま使って実行することが多かったと思います。
パッケージを使うようになると、次の点が少し変わります。

項目パッケージなしパッケージあり
保存場所作業中のフォルダ直下パッケージ名と同じフォルダ内
コンパイルjavac Sample2.javajavac pa\Sample2.java
実行java Sample2java pa.Sample2

見た目は少し複雑になりますが、これはクラスの住所がはっきりしたからです。
むしろ、大きなプログラムではこのほうが自然で、整理しやすくなります。

同じパッケージのクラスは使いやすい

このサンプルでは、Fighter クラスと Sample2 クラスがどちらも pa パッケージに含まれています。
同じパッケージに属しているクラスどうしは、パッケージ名を毎回意識しなくても、そのまま使いやすいという特徴があります。

たとえば main メソッドの中では、次のように普通に使えています。

Fighter fighter1 = new Fighter();
fighter1.setFighter("悟空", 9000);
fighter1.show();

ここで pa.Fighter と書かなくても使えているのは、同じ pa パッケージ内のクラスだからです。
同じグループの中にいる仲間のクラスは、比較的スムーズに利用できるというイメージで捉えると理解しやすいです。

パッケージ名とフォルダ名が対応する理由

パッケージ学習では、なぜフォルダ名まで合わせる必要があるのか不思議に感じることがあります。
これは、Javaがクラスの場所をパッケージ名と対応づけて管理しているからです。

たとえば、package pa; と書かれているなら、そのクラスは pa という場所にあるものとして扱われます。
そのため、実際の保存場所も pa フォルダである必要があります。

この対応関係が崩れてしまうと、Javaはクラスの位置を正しく把握できなくなり、エラーの原因になります。

図で流れを整理してみよう

パッケージのしくみは、文章だけで読むより、フォルダとクラスの関係を図で見るととてもわかりやすくなります。
次のような図を用意すると、保存場所、コンパイル、実行の流れをひと目で確認できます。

この図では、まず作業中ディレクトリ「13」の中に pa フォルダがあり、その中に Sample2.java が保存されている様子を表します。
そして、そのソースファイルをコンパイルすると、同じ pa フォルダの中に Fighter.class と Sample2.class が作られる流れが示されています。

実行するときには java pa.Sample2 と入力することによって、pa パッケージに属する Sample2 クラスを指定します。

package を指定しないとどうなるのか

ここもとても大事なポイントです。
ソースファイルに package 文を書かない場合、そのクラスは名前のないパッケージに含まれます。

これまでの学習で、パッケージを特に書かなくてもクラスを使えていたのは、その多くが同じ名前のないパッケージの中にあったからです。
だから、特別にパッケージを意識しなくても動かせていました。

ただし、プログラムが大きくなってくると、名前のないパッケージのままでは整理が追いつきにくくなります。
そこで、きちんと名前をつけたパッケージを使って、クラスをグループ分けしていく必要が出てくるのです。

学習の最初に押さえておきたいこと

パッケージの基本では、まず次の点をしっかり押さえておくと安心です。

覚えておきたいこと内容
package 文を書くファイルの先頭で所属パッケージを指定する
フォルダ名を合わせるパッケージ名と同じ名前のフォルダを作る
作業中フォルダからコンパイルするjavac pa\Sample2.java の形で指定する
実行時はパッケージ名も書くjava pa.Sample2 の形で指定する
同じパッケージのクラスは使いやすい同じグループ内で自然に利用できる

パッケージを理解すると次が見えやすくなる

パッケージの基本を理解すると、その先に学ぶ

  • 名前空間
  • サブパッケージ
  • インポート

といった内容もつながって見えてきます。

クラスが増えるほど、整理する工夫が必要になります。
その最初の一歩が、今回のパッケージです。

パッケージは、一見すると少しややこしそうに見えるかもしれません。けれども、考え方の中心はとてもシンプルです。
クラスをグループ分けして、どこに属しているかをはっきりさせる
まずはこの感覚をしっかりつかむことが大切です。

ここがわかると、大きなJavaプログラムの見通しがぐっとよくなってきます。