Javaプログラミング入門

12. パッケージ

最終更新:

javatutorial

- view
管理者のみ編集可
これまでの学習では、すべてのプログラムコードを1つのファイルに書いてきました。
しかし、実際のプログラム開発では、多くのプログラム部品を組み合わせて大きなプログラムを作ります。
全て1つのファイルに書くと、非常に長いプログラムになってしまい、読みにくく、修正も困難になります。
そこで、それぞれの役割ごとに別々のファイルに分けて作成することが重要になります。
そこで重要になるのがパッケージという仕組みです。

ファイルの分割について理解しよう

ファイル分割の実践例

実際にファイルを分割してプログラムを作成してみましょう。
まず、計算を行う処理を作成します。

サンプル:Calculator.java
  1. public class Calculator {
  2. // 2つの数値を足し算するメソッド
  3. public static int add(int a, int b) {
  4. return a + b;
  5. }
  6.  
  7. // 2つの数値を引き算するメソッド
  8. public static int subtract(int a, int b) {
  9. return a - b;
  10. }
  11.  
  12. // 2つの数値を掛け算するメソッド
  13. public static int multiply(int a, int b) {
  14. return a * b;
  15. }
  16.  
  17. // 2つの数値を割り算するメソッド
  18. public static double divide(int a, int b) {
  19. return (double) a / b;
  20. }
  21.  
  22. // 平均値を計算するメソッド
  23. public static double average(int[] numbers) {
  24. int sum = 0;
  25. for (int i = 0; i < numbers.length; i++) {
  26. sum += numbers[i];
  27. }
  28. return (double) sum / numbers.length;
  29. }
  30. }
次に、このCalculatorを使用するmainメソッド専用ファイルを作成します。

サンプル:CalculatorMain.java
  1. public class CalculatorMain {
  2. public static void main(String[] args) {
  3. int num1 = 20;
  4. int num2 = 5;
  5.  
  6. System.out.println("数値1: " + num1);
  7. System.out.println("数値2: " + num2);
  8. System.out.println();
  9.  
  10. // Calculatorに定義したメソッドを使用
  11. int addResult = Calculator.add(num1, num2);
  12. int subResult = Calculator.subtract(num1, num2);
  13. int mulResult = Calculator.multiply(num1, num2);
  14. double divResult = Calculator.divide(num1, num2);
  15.  
  16. System.out.println("足し算の結果: " + addResult);
  17. System.out.println("引き算の結果: " + subResult);
  18. System.out.println("掛け算の結果: " + mulResult);
  19. System.out.println("割り算の結果: " + divResult);
  20. }
  21. }
実行結果:
数値1: 20
数値2: 5
 
足し算の結果: 25
引き算の結果: 15
掛け算の結果: 100
割り算の結果: 4.0
このように、ファイル名.メソッド名()の形式で他のファイルに定義してあるメソッドを呼び出すことができます。
また、この記法により、どのファイルに定義されているメソッドを使用しているかが明確になります。

ファイル分割のメリット

ファイルを分割することで、以下のようなメリットがあります。

①役割の明確化
それぞれのファイルが特定の役割を持つため、プログラムの構造が理解しやすくなります。
計算に関する処理はCalculatorファイル、表示に関する処理は別のファイルというように、責任が明確に分かれます。

②再利用性の向上
一度作成したプログラム部品は、mainメソッドだけでなく、他のメソッド内でも使用できます。
例えば、Calculatorは計算機能が必要な他のメソッドでも活用することができます。

③保守性の向上
特定の機能を修正したい場合、該当する処理のみを変更すれば済みます。
計算ロジックを変更したい場合はCalculatorに定義されたメソッド内の処理だけを修正すれば、それを使用している全ての場所に変更が反映されます。

④チーム開発の効率化
複数人でプログラムを開発する場合、それぞれが異なる役割のファイルを担当することで、並行して作業を進めることができます。

パッケージとは

プログラムが大きくなってファイルの数が増えてくると、今度は「どのファイルがどこにあるのか分からない」という問題が発生します。
これは、本棚に本がバラバラに置かれている状態と似ています。
この問題を解決するのがパッケージです。

パッケージとは、関連するファイルをグループ分けして整理するための仕組みです。
現実世界で例えると、パッケージは「フォルダ」や「本棚の棚」のようなものです。
「数学」の棚には数学に関する本を置く、「国語」の棚には国語に関する本を置く、「理科」の棚には理科に関する本を置くというように整理します。

プログラムでも同様に、「計算」パッケージには計算に関するファイルを置く、「表示」パッケージには表示に関するファイルを置く、「データ」パッケージにはデータ管理に関するファイルを置くというように整理することができます。

パッケージの作成方法

パッケージの作成と使用

パッケージを作成するには、プロジェクト内で右クリックをして
新規(W)項目からパッケージ項目をクリックします。

そしたら新規のJavaパッケージウィンドウが立ち上がります。

mathという名前のパッケージを作成したいので、名前の入力欄に「math」と入力してください。

「完了」ボタンを押すと、mathというパッケージが作成されているのがわかるかと思います。

そして、対象のパッケージに所属しているということを示すために
Javaファイルの先頭package文を記述します。
package パッケージ名;
実際にパッケージを使ったプログラムを作成してみましょう。
まず、「math」というパッケージに計算用のメソッドを定義するファイルを作成します。

サンプル:math/Calculator.java
  1. package math;
  2.  
  3. public class Calculator {
  4. // 2つの数値を足し算するメソッド
  5. public static int add(int a, int b) {
  6. return a + b;
  7. }
  8.  
  9. // 2つの数値を引き算するメソッド
  10. public static int subtract(int a, int b) {
  11. return a - b;
  12. }
  13.  
  14. // 2つの数値を掛け算するメソッド
  15. public static int multiply(int a, int b) {
  16. return a * b;
  17. }
  18.  
  19. // 2つの数値を割り算するメソッド
  20. public static double divide(int a, int b) {
  21. return (double) a / b;
  22. }
  23.  
  24. // 平均値を計算するメソッド
  25. public static double average(int[] numbers) {
  26. int sum = 0;
  27. for (int i = 0; i < numbers.length; i++) {
  28. sum += numbers[i];
  29. }
  30. return (double) sum / numbers.length;
  31. }
  32. }

package文を記載したらこのようなエラーが発生したと思います。

現在の段階ではmathパッケージではなく、別のパッケージ(今だとデフォルト・パッケージという場所)の中に
このCalculator.javaが含まれてしまっているので
間違ったpackage文を記載したのではないのか?とエラーになってしまいます。
なので、赤の波線が引かれている部分にカーソルを合わせて、Eclipseにエラーを解消してもらいましょう。
'Calculator.java'をパッケージmathに移動】という項目を選択してください。

そうすると、エラーが解消されて、mathパッケージの中にCalculator.javaが移動しているのがわかるかと思います。

では次に同じ手順で、「util」というパッケージに便利な機能を提供するメソッドを定義するファイルを作成します。

サンプル:util/ArrayHelper.java
  1. package util;
  2.  
  3. public class ArrayHelper {
  4. // 配列の中身を表示するメソッド
  5. public static void printArray(int[] array) {
  6. System.out.print("配列の中身: ");
  7. for (int i = 0; i < array.length; i++) {
  8. System.out.print(array[i]);
  9. if (i < array.length - 1) {
  10. System.out.print(", ");
  11. }
  12. }
  13. System.out.println();
  14. }
  15.  
  16. // 配列の最大値を求めるメソッド
  17. public static int findMax(int[] array) {
  18. int max = array[0];
  19. for (int i = 1; i < array.length; i++) {
  20. if (array[i] > max) {
  21. max = array[i];
  22. }
  23. }
  24. return max;
  25. }
  26. }

パッケージの階層構造

パッケージの階層構造を理解するために、まず身近な例から考えてみましょう。

図書館では、膨大な数の本を効率よく管理するために、分野別に整理していますね。
例えば、以下のような分類になっています。

文学 → 日本文学 → 小説 → 夏目漱石の作品
理工学 → 数学 → 数I → 初級者向けの教科書
歴史 → 日本史 → 江戸時代 → 徳川家に関する書籍

このように、大きなカテゴリから小さなカテゴリへと段階的に分類することで、目的の本を素早く見つけることができます。

プログラムの世界でも、同じような問題が発生します。
大きなシステムを作ると、何百、何千というファイル(プログラムの部品)が必要になります。
これらをすべて同じ場所に置いてしまうと、以下のような問題が起こります。

目的のファイルを見つけるのが困難になる
1000個のファイルが同じフォルダにあったら、必要なものを探すのに時間がかかってしまいます。

同じ名前のファイルが作れない
例えば、社員情報を管理する「Employee.java」と
顧客情報を管理する「Employee.java」を作りたい場合、同じ名前では区別できません。
また、Javaの仕様で同じパッケージ内に同じ名前のファイルを作成することができません。

このような階層構造にすることで、以下のような利点があります。

整理整頓された構造
どこに何があるかが一目で分かりるようになります。

名前の衝突回避
異なるパッケージなら同じファイル名を使用できます。

例えば、会社のシステムを作る場合
com.company.employee(社員管理関連)
com.company.sales(営業関連)
com.company.accounting(経理関連)というように
ドット(.)で区切って階層を表現します。

実際に、下記のサンプルと同じようなパッケージの階層構造を作成してみましょう。
パッケージの作成方法自体は、先程のmathパッケージと同じです。
ただ名前の部分にドット(.)で区切って階層構造を記載する必要があります。

「完了」ボタンをクリックすると、com.company.mathというパッケージが作成されているのがわかるかと思います。

そして、そのパッケージの中にAdvancedCalculator.javaを作成していきましょう。



サンプル:com/company/math/AdvancedCalculator.java
  1. package com.company.math;
  2.  
  3. public class AdvancedCalculator {
  4. // 累乗を計算するメソッド
  5. public static double power(double base, int exponent) {
  6. double result = 1.0;
  7. for (int i = 0; i < exponent; i++) {
  8. result *= base;
  9. }
  10. return result;
  11. }
  12.  
  13. // 階乗を計算するメソッド
  14. public static long factorial(int n) {
  15. if (n <= 1) {
  16. return 1;
  17. }
  18. long result = 1;
  19. for (int i = 2; i <= n; i++) {
  20. result *= i;
  21. }
  22. return result;
  23. }
  24. }
Eclipseの画面だと階層構造というのが分かりづらいですが
実際のフォルダとファイルの階層構造は以下の画像のようになっています。






こちらは、プロジェクトを右クリックして「Windowsエクスプローラ」項目をクリックすると確認することができます。

import文

現在、CalculatorMain.javaに今まで作成したメソッドたちを使おうとすると
以下の画像のように大量のエラーが発生してしまいます。

他のパッケージにあるファイルに定義されているメソッドを使用するには、import文を使用します。
import文は、「このパッケージのこのファイルを使いたい」ということをJavaに伝える仕組みです。

import文の基本的な書き方は以下の通りです。
import パッケージ名.ファイル名;
実際にimport文を使ったプログラムを作成してみましょう。

サンプル:CalculatorMain.java
import math.Calculator;
import util.ArrayHelper;
import com.company.math.AdvancedCalculator;
 
public class CalculatorMain {
    public static void main(String[] args) {
        // 基本的な計算
        int result1 = Calculator.add(10, 20);
        int result2 = Calculator.subtract(50, 15);
        System.out.println("10 + 20 = " + result1);
        System.out.println("50 - 15 = " + result2);
        System.out.println();
 
        // 配列の操作
        int[] scores = {85, 92, 78, 96, 88};
        ArrayHelper.printArray(scores);
 
        double avg = Calculator.average(scores);
        int max = ArrayHelper.findMax(scores);
        System.out.println("平均点: " + avg);
        System.out.println("最高点: " + max);
        System.out.println();
 
        // 高度な計算
        double powerResult = AdvancedCalculator.power(2, 8);
        long factorialResult = AdvancedCalculator.factorial(5);
        System.out.println("2の8乗 = " + powerResult);
        System.out.println("5の階乗 = " + factorialResult);
    }
} 

10 + 20 = 30
50 - 15 = 35
 
配列の中身: 85, 92, 78, 96, 88
平均点: 87.8
最高点: 96
 
28= 256.0
5の階乗 = 120

ワイルドカードimport

同じパッケージから複数のファイルのメソッドを使用する場合、アスタリスク(*)を使ってそのパッケージの全てのファイルをimportすることができます。
import パッケージ名.*;
例:
import util.*;  // utilパッケージの全てのプログラム部品をimport
ただし、どのファイル(プログラム部品)を使用しているかが分かりにくくなるため、基本的には個別にimportすることが推奨されます。

import文の記述位置

import文は、以下の順序で記述する必要があります。

1. package文(そのファイルがどのパッケージに属するか)
2. import文(使用する他のパッケージのプログラム部品)
3. プログラム部品の定義

package com.example.myapp;
 
import java.util.Scanner;
import math.Calculator;
 
public class MyClass {
    // プログラム部品の内容
} 

完全限定名

これまで、import文を使って他のパッケージのプログラム部品を使用してきました。
しかし、import文を使わずに他のパッケージのプログラム部品を使用する方法もあります。
それが完全限定名です。

完全限定名とは、パッケージ名を含めたプログラム部品の正式な名前のことです。
これは、住所を使って特定の家を指定するのと似ています。
例えば、「田中さん」だけでは、どの田中さんか分からないが、「東京都渋谷区1-2-3の田中さん」なら、特定の田中さんを指すことができます。

プログラムでも同様に、「Calculator」だけでは、どのパッケージのCalculatorか分からない場合がある一方で、「math.Calculator」なら、mathパッケージのCalculatorを明確に指すことができます。

完全限定名の使用例

import文を使わずに、完全限定名でプログラム部品を使用してみましょう。

サンプル:FullyQualifiedMain.java
public class FullyQualifiedMain {
    public static void main(String[] args) {
        // 完全限定名を使ってメソッドを呼び出し
        int addResult = math.Calculator.add(15, 25);
        int subResult = math.Calculator.subtract(100, 30);
 
        System.out.println("15 + 25 = " + addResult);
        System.out.println("100 - 30 = " + subResult);
 
        // 配列の操作
        int[] numbers = {10, 20, 30, 40, 50};
 
        // 完全限定名を使用
        util.ArrayHelper.printArray(numbers);
        int maxValue = util.ArrayHelper.findMax(numbers);
        System.out.println("最大値: " + maxValue);
 
        // 高度な計算も完全限定名で
        double result = com.company.math.AdvancedCalculator.power(3, 4);
        System.out.println("3の4乗 = " + result);
    }
}
 
実行結果
15 + 25 = 40
100 - 30 = 70
配列の中身: 10, 20, 30, 40, 50
最大値: 50
34= 81.0

デフォルトパッケージ

デフォルトパッケージが推奨されない理由

学習段階では便利なデフォルトパッケージですが、実際の開発現場では使用が推奨されていません

身近な例で説明すると、学校の教室を想像してみてください。
クラス分けをせずに全学年の生徒を一つの大きな教室に入れてしまうと、どのような問題が起こるでしょうか。
1年生から6年生まで全員が同じ教室にいると、誰がどの学年なのか分からなくなり、授業の管理も困難になってしまいます。

Javaの開発でも同様の問題が発生します。
デフォルトパッケージを使い続けることで、以下のような深刻な問題が生じる可能性があります。

名前の衝突問題

デフォルトパッケージでは、同じ名前のクラスを複数作成することができません。
これは、同じ教室に同じ名前の生徒が二人いると混乱するのと同じです。

コードの整理が困難

デフォルトパッケージを使用すると、すべてのクラスが同じ場所に配置されるため
プログラムが大きくなるにつれて管理が非常に困難になります。

これは、家の中のすべての物を玄関に置いてしまうような状況です。
最初は物が少ないので問題ありませんが、物が増えるにつれて以下のような問題が発生します。

他のパッケージからアクセスできない問題

デフォルトパッケージには、学習を進めていく上で必ず知っておくべき重大な制限があります。
それは、デフォルトパッケージに配置されたクラスは、他のパッケージから参照することができないという問題です。
つまり、import文、完全限定名を記載することができません
デフォルトパッケージ内のクラス同士は互いを利用できますが、他のパッケージからは全くアクセスできないのです。
学習が進んで、適切なパッケージ構造を使用するようになった時
これまでデフォルトパッケージで作成したクラスが完全に使用不可能になってしまいますので気をつけるようにしましょう。
そのため、Javaのファイルに関しては何かしらのパッケージに所属させておくことが推奨されています。

ウィキ募集バナー