パッケージ間のアクセス制御に慣れたいなと思い勉強し直すことにしました。
パッケージ(package)とは
パッケージ(package)とはJavaのクラスを分類するための仕組みです。
グループ化というとわかりやすいかもしれません。クラスに名前空間を提供したりアクセス制御としても使われます。
クラスを探しやすくなる便利な機能です。
また、例えばprintというクラスがあったとして、パッケージが分かれていないと同じ名前を使用することはできません。
大規模になれば何人かで作業することもありますが、パッケージを分けて作業すれば同じ名前のクラスを作成しても問題が起きないという利点もあります。
パッケージの宣言
ファイルには自分の属するパッケージ名を宣言します。
package パッケージ名;
パッケージ名は大文字にも対応していますが小文字で作成するのが一般的だそうです。
階層が深くなった場合はドットで区切ります。
package パッケージ名.サブパッケージ名;
階層が一つ下がったパッケージをサブクラスと言います。
階層が深くなった分だけドットでつないで書くことができます。
ちなみに大規模なシステムを開発する場合、パッケージ名が同じ場合が発生してしまうことがあります。
パッケージ名が重複することを避けるため、ある一意の値を元にパッケージ名を作成する習慣があります。
その一意の値はドメインです。ドメインはネット上の住所の役割を担っているため1つしかありません。
本サイトのドメインの場合は「https://programmer-life.work/」なので以下のようにパッケージが作成されます。
work.programmer_life
逆さに書くのが通例です。
追記
ハイフンは使えないためアンダースコアに変えました。(hitさんコメントより修正。以下同様)
使用できる英字記号は「_」のみです。
(※「$」マークも可能だが、機械的に生成されたソースコードでのみ使用される。またレガシー システムの既存の名前に使われている。)
参考:https://docs.oracle.com/javase/specs/jls/se19/html/jls-3.html#jls-3.8
importの宣言
他のパッケージのクラスを使用する時にインポートを宣言します。
その際の書き方は以下のようになります。
import パッケージ名.サブパッケージ名.クラス名;
import java.util.*;なんてのはよく見かけると思います。
※「*」はオンデマンドインポートで上記の場合java.utilパッケージに含まれている全てのクラスとインターフェイスをimportします。
オンデマンドインポートは名前の衝突を引き起こす可能性があるため単一型インポートが基本です。
宣言の順番
パッケージの宣言は必ず先頭に書きます。
書く順番としてはとしては「package」→「import」→「class」となります。
package work.programmer_life;
import java.util.*;
public class test{ }
パッケージの種類
パッケージには2つ種類があります。
名前のあるパッケージと名前のないパッケージです。
名前のないパッケージのことをデフォルトパッケージと言い(無名パッケージとも言う)、名前がない場合はデフォルトパッケージに属します。
(パッケージを宣言していない場合は、デフォルトパッケージとなります。)
デフォルトパッケージは練習などの簡単なプログラミングに使われ、規模の大きなプログラムに使用されることはまずないそうです。
パッケージのアクセス
ここではアクセス修飾子の深堀はせずパッケージ間のアクセス制御について学びます。
アクセス制御については以下でまとめています。
パッケージ間のアクセスについてこちらではデフォルトかそうでないかで分けて説明していきます。
名前のついたパッケージであれば、import宣言をすることでクラスをimportし使用することができます(またはフルパスで明記)。
ではpackage文の記載されていないデフォルトパッケージに属するクラスはどのように呼び出すのでしょうか?
結論から言えば基本的に呼び出すことはできません。
(無理やりリフレクションAPIで呼び出すことができますが、使い方注意)
サンプルコードを用意したので興味のある方は実験してみると動きがわかって楽しいかと思います。
サンプルコード デフォルトパッケージはデフォルトパッケージにしかアクセスできない
デフォルトパッケージに属するクラスへは同じデフォルトパッケージに属するクラスしか呼び出せません。
デフォルトパッケージ
┝A.java
└B.java
A.java
public class A {
public static void test(){
System.out.println("default package");
}
}
B.java (Run用コード)
public class B {
public static void main(String[] arg){
A.test(); //呼び出せる
}
}
結果
default package
同じデフォルトパッケージに属するクラスしか呼び出せないことから大規模なシステムへは不向きと言われています。(Eclipseでも非推奨のwarningがでます。)
※ デフォルトパッケージにクラスを作る場合module-info.java
がある場合はコンパイルに失敗します。
サンプルコード 名前のあるパッケージからデフォルトパッケージは呼び出せない
work.programmer_lifeパッケージに属しているクラスからデフォルトパッケージに属するクラスは呼び出せません。
デフォルトパッケージ
└A.java
work.programmer_life
└B.java
A.java
public class A {
public static void test(){
System.out.println("default package");
}
}
B.java (コンパイルエラー)
package work.programmer_life;
public class B{
public static void main(String[] arg){
A.test(); //コンパイルエラー
}
}
Aクラスを参照できないためコンパイルエラーとなります。
サンプルコード 名前のあるパッケージ間のアクセス
同じパッケージに属するクラスへはアクセス可能です。パッケージ名が異なってもimportすれば使用できます。
work.programmer_life
┝A.java
└B.java
other
└A2.java
A.java
package work.programmer_life;
public class A{
public static void test(){
System.out.println("work.programmer_life package");
}
}
A2.java
package other;
public class A2{
public static void test(){
System.out.println("other package");
}
}
B.java (Run用コード)
package work.programmer_life;
import other.A2;
public class B{
public static void main(String[] arg){
A.test(); //呼び出せる
A2.test();//呼び出せる
}
}
結果
work.programmer-life package
other package
import文を書くことでパッケージ名が異なってもアクセスが可能になることがわかります。
サンプルコード クラスの名前が重複した場合
パッケージが異なってもクラス名が同じだとインポートしたクラスが優先され同一パッケージのクラスを呼び出せなくなるので注意しましょう。その場合はフルパスを書くことで回避できます。
work.programmer_life
┝A.java
└B.java
other
└A.java
A.java (work.programmer-life)
package work.programmer_life;
public class A{
public static void test(){
System.out.println("work.programmer_life package");
}
}
A.java(other)
package other;
public class A{
public static void test(){
System.out.println("other package A");
}
}
B.java
package work.programmer_life;
public class B{
public static void main(String[] arg){
A.test(); //呼び出せる
other.A.test();//呼び出せる
}
}
結果
work.programmer-life package
other package A
other.A.test()とパッケージ名も含めて書くことで、同じクラス名でもパッケージの異なるクラスを呼び出せます。
おわり
クラスやメソッドのアクセス制御でpublic、protected、privateなどと覚えたので、パッケージにもpublicなどあると勘違いしていました。
デフォルトパッケージもリフレクションAPIというのを使うとアクセスできるらしいですが、イレギュラーらしいので初心者の身としては使うことはなさそうなので特に深ぼりしませんでした。
参考
https://www.geeksforgeeks.org/packages-in-java/
What’s the syntax to import a class in a default package in Java? [duplicate]
パッケージ名にハイフンを使う事は出来ないのではないですか?
エラーが出てコンパイルが通りません。
アンダースコアに置き換えたら通りました。
javac version 1.8.0です。
hitさん
ご指摘ありがとうございます。
>>パッケージ名にハイフンを使う事は出来ないのではないですか?
ごめんなさい、作れないです。誤字です。
修正します。
わざわざコメントいただきありがとうございます。