最初はよいのですが、作成を重ねていくと、ディレクトリ(もしくはフォルダ、ここではディレクトリと呼びます)がJavaファイルだらけになってしまいます。Javaも通常のファイルシステムと同様にディレクトリを利用することができます。目的を持ってディレクトリを利用することでファイルの管理が容易になるため積極的に利用します。目的を持ってディレクトリを利用することをJavaではパッケージと呼びます。
パッケージを使用する際の記述順ですが「package パッケージ名;」になります。パッケージを設定することを「パッケージ宣言」と呼びます。パッケージ宣言はファイルの先頭に記述します。具体例を見てみましょう。
package hogehoge;
class Car {
public static void main(String[] args) {
System.out.println("Car");
}
}
今回は便宜上パッケージ名を「hogehoge」にしましたがパッケージ名もクラス名、メソッド名、変数名等と同様に意味のある名称をつけましょう。またディレクトリと同様に階層構造を持たせることができます。その場合パッケージ名をピリオドでつなぐことで階層構造を表現することができます。具体例を見てみましょう。
package foo.bar;
class Bicycle {
public static void main(String[] args) {
System.out.println("Bicycle");
}
}
このクラスをコンパイルしてみます。コンパイルの際はパッケージがディレクトリであるという特性から、「hogehoge」ディレクトリを作成しその中に「Car.java」を配置します。
>dir hogehoge\Car.java
ドライブ D のボリューム ラベルは Data です
ボリューム シリアル番号は BC0E-266A です
D:\hogehoge のディレクトリ
2013/01/08 21:03 35 Car.java <-ファイルが存在しています。
1 個のファイル 35 バイト
0 個のディレクトリ 306,899,439,616 バイトの空き領域
>javac hogehoge\Car.java
>
このクラスを実行します。パッケージの特性からクラス名の先頭にパッケージ名を付加します。パッケージ名とクラス名の間にピリオドを付加し実行します。
パッケージ名を含めたクラス名のことを完全修飾クラス名と呼びます。完全修飾クラス名を英語では「Fully Qualified Class Name」と呼びます。これでは長いのでFQCNと表現することもあります。
上記の「hogehoge」パッケージの「Car」クラスのFQCNは「hogehoge.Car」、「foo.bar」パッケージの「Bicycle」クラスのFQCNは「foo.bar.Bicycle」になります。プログラム上でクラスを表現する際には、FQCNで記述します。
作成したJavaクラスを将来的に配布するようになるのですが、パッケージ名が他のプログラムのパッケージ名と重複しないとは限りません。
パッケージ名が重複しないためにパッケージ名の決め方に慣例があります。プログラマが所属する会社や組織のドメイン名を使用します。Yahoo様を例にするとドメイン名がyahoo.co.jpなのでパッケージ名はjp.co.yahoo.hogehoge等とします。
ここまででパッケージについて学びましたが、クラス名が重複、ディレクトリの中身がたくさんのファイルになる、という事さえ回避できれば、パッケージはいらないのでは?実際にコンパイルや実行の際にパッケージ名が付加されていると、ディレクトリを作成したり、コンパイルのコマンドも長くなりがちですし、良いことがあるようには思えません。実はパッケージはディレクトリを整理するためにある機能ではないのです。
パッケージを分けることで、メソッドの利用や、変数の利用を規制することができるのです。このことをJava言語では「可視性」や「アクセス制限」と呼びます。具体的にみてみましょう。
アクセス修飾子 | 内容 |
---|---|
private | 同じクラスからしか利用できない。 |
(記述しない、省略) | そのクラスと同じパッケージ内からしか利用できない。 |
protected | そのクラスと同じパッケージ、もしくはそのクラスのサブクラスからしか利用できない。 |
public | 特に制限はなく自由に利用できる。 |
見慣れたキーワード「public」がここで登場しました。今までの「public static void main(String[])」は「制限なく呼び出すことができ」「インスタンス化していなくても呼び出すことができ」「戻り値のない」「mainという名称の」「文字列配列をパラメータに持つ」メソッドという意味になるのです。
具体例をみながら確認しましょう。便宜上(記述しない、省略)を表現するために(省略)と表記します。
package aaa;
public class Aaa {
// このクラスはpublicクラスなので、制限なくインスタンス化できる。
private String color; // private変数なので、このクラスからしか利用できない。
public String getColor() {
// publicメソッドなので、制限なく利用できる。
return color;
}
protected void setColor(String c) {
// protectedメソッドなので、aaaパッケージのクラスか、Aaaのサブクラスなら利用できる。
color = c;
}
}
package bbb;
class Bbb {
public static void main(String[] args) {
aaa.Aaa a = new aaa.Aaa(); // 「aaa.Aaa」クラスはpublicクラスなので、Bbbクラスから利用できる。
// 「aaa.Aaa#getColor()」メソッドはpublicメソッドなので、Bbbクラスから利用できる。
System.out.println(a.getColor());
// 「aaa.Aaa#setColor(String)」メソッドはprotectedメソッドなので、Bbbクラスから利用できない。
a.setColor("red");
// 「aaa.Aaa#color」変数はprivateメソッドなので、Bbbクラスから利用できない。
a.color = "red";
}
}
実際にプログラムを作成する際、パッケージ名が長いとプログラム作成に時間が余計にかかります。このようなことに対応するためJava言語ではパッケージ名を省略する方法が用意されています。その方法を「インポート宣言」と呼び、記述順ですが「import 省略したいFQCN名;」になり、パッケージ宣言の直後に記述します。この記述を行うと記述以降のプログラムではクラス名を書くだけでFQCNを指定したことになります。
具体的な記述方法を見てみましょう。
package bbb;
import aaa.Aaa; // インポート宣言したのでコレ以降Aaaクラスと記述されるとaaa.Aaaのことを指します。
class Bbb {
public static void main(String[] args) {
Aaa a = new Aaa(); // 以前はインポート宣言されていなかったためFQCNで記述していた。
System.out.println(a.getColor());
}
}
実はインポート宣言にはもうひとつの方法があります。パッケージ全体をインポートすることです。この方法のことを「オンデマンドの型インポート宣言」と呼びます。記述順ですが「import 省略したいパッケージ名.*;」になります。
パッケージ全体をインポートできるので記述が容易になるのですが、反面どのパッケージのクラスを利用しているか把握しにくくなるのです。詳細は説明しませんがクラス単独のインポート宣言を利用するようにしましょう。
こちらも具体的な記述方法を見てみましょう。
package bbb;
import aaa.*;
class Bbb {
public static void main(String[] args) {
Aaa a = new Aaa();
System.out.println(a.getColor());
}
}
先の説明では「単独型のインポート宣言を利用しなさい。」とありました。オンデマンド型の不利な点があるからなのですが、いったいどのように不利なのでしょうか。