いくつかのJavaクラスを書いても問題ありません。もう少し書いたらどうでしょう。そしてプロジェクトに200ものファイルができ、どこに何があるのかわからなくなります。2つのクラスが同じ名前を持ち、コンパイラが文句を言い始めます。これこそがパッケージが解決する問題です。
Javaにおけるパッケージとは、名前空間のことです。関連するクラス、インターフェース、列挙型をフォルダのような構造にまとめます。Javaのすべてのクラスはパッケージ内に存在します。シンプルなHelloWorldクラスであっても、パッケージが存在します。明示的に指定しなかった場合でも、「デフォルトパッケージ」と呼ばれる場所に配置されます。これは学校のプロジェクトでは問題ありませんが、現実のアプリケーションでは避けるべきです。
公式のOracleチュートリアルでは次のように説明されています:「パッケージとは、関連する型をグループ化し、アクセス保護と名前空間管理を提供するものです。」これは要するに「パッケージはコード同士が干渉し合うのを防ぐ」という意味です。
パッケージが存在する理由
実用的な理由は3つあります。
1つ目は名前の衝突を防ぐためです。あなたがUserクラスを作成したとします。インポートしたライブラリにもUserクラスが存在する場合、パッケージがなければJavaはそれらを区別できません。パッケージを使えば、一方はcom.yourcompany.model.User、もう一方はorg.somelibrary.model.Userとなり、両者は共存できます。
2つ目はアクセス制御です。Javaには4つのアクセスレベルがあります:private、package-private(デフォルト)、protected、publicです。package-privateは初心者が見落としがちなものです。これは「同じパッケージ内のクラスからはアクセス可能だが、パッケージ外からはアクセスできない」という意味です。これはバグではなく設計上の機能です。実装の詳細を外部から隠しつつ、同じパッケージ内の関連クラスからは見える状態に保てます。
3つ目は発見しやすさです。請求関連のクラスをすべてcom.yourcompany.billingに配置すれば、新しい開発者がどこを探せばよいかがわかります。これはコンピュータ上でファイルをフォルダに整理するのと同じ理屈です。
命名規則
Javaのパッケージ名は、ドメイン名を逆にした規則に従います。もしjamilur.devを所有しているなら、パッケージ名はdev.jamilurから始まります。会社でexample.comを使用しているなら、com.exampleから始まります。
公式のJavaドキュメントでは次のように推奨されています:「パッケージ名はクラスやインターフェースの名前と衝突しないよう、すべて小文字で記述します。」
実際のプロジェクトでは次のようになります。
-
java.lang-- Java言語のコアクラス -
java.util-- コレクションなどのユーティリティクラス -
org.springframework.boot-- Spring Boot -
com.mysql.cj.jdbc-- MySQL JDBCドライバ -
dev.jamilur.ecommerce.model-- 架空のECサイトのモデル
各ドットはディレクトリを表します。dev.jamilur.ecommerce.modelはディスク上でdev/jamilur/ecommerce/model/に対応します。
パッケージの宣言
Javaファイルの先頭、import文より前に次のように記述します。
package dev.jamilur.bkash.app;
Enter fullscreen mode Exit fullscreen mode
これだけです。ファイルはパッケージ名に一致するフォルダ構造に配置する必要があります:dev/jamilur/bkash/app/。
パッケージ宣言を省略すると、Javaはクラスをデフォルトパッケージに配置します。実際のアプリケーションでは避けましょう。デフォルトパッケージ内のクラスは名前付きパッケージ内のクラスからインポートできません。つまり、プロジェクトの他の部分から自分のコードを使用できなくなります。Baeldungがデフォルトパッケージが本番環境で危険な理由を詳しく説明しています。
実際の例
バングラデシュのECサイト向けの小さなプロジェクト構造を構築してみましょう。
src/
dev/
jamilur/
ecommerce/
model/
Product.java
Customer.java
Order.java
service/
PaymentService.java
ShippingService.java
controller/
OrderController.java
Enter fullscreen mode Exit fullscreen mode
Product.javaファイルの先頭は次のようになります:
package dev.jamilur.ecommerce.model;
public class Product {
private String name;
private double price;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
}
Enter fullscreen mode Exit fullscreen mode
そしてPaymentService.javaファイルの先頭は次のようになります:
package dev.jamilur.ecommerce.service;
import dev.jamilur.ecommerce.model.Order;
public class PaymentService {
public boolean processPayment(Order order) {
// payment logic here
return true;
}
}
Enter fullscreen mode Exit fullscreen mode
import文に注目してください。他のパッケージのクラスを使用したい場合はインポートします。Orderクラスはmodelパッケージに、PaymentServiceクラスはserviceパッケージに存在します。import文がその橋渡しをします。
インポートとワイルドカード
インポートには3つの方法があります。
明示的インポート(ほとんどの場合これを使います)。
import dev.jamilur.ecommerce.model.Order;
import dev.jamilur.ecommerce.model.Customer;
Enter fullscreen mode Exit fullscreen mode
ワイルドカードインポート(便利ですが議論の的になります)。
import dev.jamilur.ecommerce.model.*;
Enter fullscreen mode Exit fullscreen mode
これはmodelパッケージ内のすべてのクラスをインポートします。タイプ量を減らせますが、代償として明確さが失われます。ファイルを見た読者は、どの特定のクラスを使用しているのかわかりません。GoogleのJavaスタイルガイドを含む多くのスタイルガイドがワイルドカードインポートを推奨していません。IDEは自動的に展開してくれるでしょう。
静的インポート(稀に使用、注意して使います)。
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
Enter fullscreen mode Exit fullscreen mode
これでMath.PIの代わりにPI、Math.sqrt(16)の代わりにsqrt(16)と書けるようになります。定数やユーティリティメソッドには便利ですが、使いすぎるとコードが読みにくくなります。
パッケージプライベートアクセス
ほとんどのチュートリアルが省略するアクセス修飾子について説明します。
クラス、メソッド、フィールドを宣言する際にアクセス修飾子(public、private、protected)を付けないと、パッケージプライベートアクセスになります。同じパッケージ内のものはすべて見えますが、パッケージ外のものは一切見えません。
package dev.jamilur.ecommerce.service;
class PaymentGatewayHelper {
// no 'public' keyword
// only classes in the same package can use this
String encryptData(String raw) {
return "encrypted_" + raw;
}
}
Enter fullscreen mode Exit fullscreen mode
これは公開APIの一部にすべきでない内部ヘルパークラスに便利です。Springはこのパターンを多用しています。サービスメソッドに@Transactionalを付け、パッケージプライベートのままにしておけば、同じパッケージ内に存在するためSpringのプロキシがインターセプトできます。ただし外部のコードから直接呼び出すことはできません。
パッケージとモジュール
Java 9ではモジュールが導入されました。これはパッケージを強化したようなものです。モジュールは複数のパッケージを束ね、どのパッケージをエクスポートし、どのモジュールを必要とするかを宣言します。
日常的なプロジェクトではモジュールは必要ありません。ほとんどのアプリケーションではパッケージで十分です。モジュールは大規模システムでのみ価値を発揮する複雑さを追加します。Java Platform Module System (JPMS)については、公式JModドキュメントで後から調べることができます。
初心者がよく混乱する点
パッケージ名はパフォーマンスに影響しますか? いいえ。パッケージは純粋に組織化のためのものであり、実行時のコストはゼロです。
異なるパッケージ内の2つのファイルで同じクラス名を使えますか? はい。それこそがパッケージが存在する主な理由の1つです。com.example.Userとcom.other.Userは完全に異なるクラスです。
パッケージ名とフォルダ構造は一致させる必要がありますか? はい。標準的なJavaコンパイルではそのようにする必要があります。コンパイラはそれを期待します。MavenやGradleなどのモダンなビルドツールはこれを強制します。
後からパッケージ名を変更できますか? 変更は可能ですが、すべてのimport文と参照が壊れます。IDEは自動的にリファクタリングできます。ただし、チームで共有している場合、パッケージ名の変更はマージコンフリクトや混乱を引き起こします。パッケージ構造は早い段階で決めておきましょう。
クイックサマリー
- パッケージは、関連するJavaの型をグループ化する名前空間です。
- パッケージ名は逆ドメイン形式に従います:
com.example.project.module。 - パッケージ宣言はJavaファイルの最初の行です。
- 他のパッケージのクラスは
importキーワードを使ってインポートします。 - 明確さのために、ワイルドカードインポートより明示的インポートを優先しましょう。
- パッケージプライベートアクセス(修飾子なし)は、同じパッケージ内でのみアクセスを許可します。
- パッケージはファイルシステム上のディレクトリにマッピングされます。
- モジュール(Java 9以降)は、大規模システム向けのパッケージの上に位置する追加レイヤーです。
パッケージは派手なものではありません。しかし、スケールするプロジェクトと、誰もナビゲートできないファイルの山との違いを生み出します。パッケージ構造を早い段階で正しく設計しておけば、後々の頭痛を減らせます。
dev.java/learnに基づく:Packages
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.