こんにちは。まさきです。
今回はJavaで頻繁に使用するpublicやprivateなどの解説をしていきます。
publicなどは意味を理解していないがいつもとりあえず書いている方も多いと思います。
これらは簡単にいうと、publicなどを付けたクラスやメソッドを他のクラスからアクセスを許可するかしないかの指定ができます。
アクセス修飾子とは
アクセス修飾子とは上でも書いた通り、他のクラスからアクセスを許可するかの指定ができます。
アクセス修飾子を一番見るのは以下のmain構文でしょうかね。
public static void main(String[] args){
//処理内容
}
こちらのmain構文ではpublicが使われていますので、どのクラスからでもアクセスができるように指定されています。
Javaを覚えたてのころは呪文のように書くこの構文もちゃんと意味があるんですよね。
他には値を更新しない変数(定数と呼ぶ)はprivateを付けた方がよいと言われます。
定数はそのクラスやメソッドでしか使用しないため、他のクラスから呼び出す必要がなく、変に利用されるのを防ぐためです。
保守性や可読性にも繋がっていきます。
上記のように、どこの範囲からまでアクセスを許可するかを指定するのがアクセス修飾子です。
こちらがアクセス修飾子の指定個所とアクセスできる範囲です。
上がどこからでもアクセスができ、下に行くほど狭くなっていきます。
指定なしも意味があるので学習でJavaを書く場合はpublicを付けるのをおすすめします。
アクセス修飾子 | クラス | コンストラクタ | メンバ変数 | メソッド | 説明 |
public | 〇 | 〇 | 〇 | 〇 | どのクラスからも利用できる。 |
protected | ✕ | 〇 | 〇 | 〇 | このクラスを継承したサブクラスと同一パッケージ内のクラスから利用できる。 |
デフォルト(指定なし) | 〇 | 〇 | 〇 | 〇 | 同一パッケージ内のクラスから利用できる。 |
private | ✕ | 〇 | 〇 | 〇 | 同一クラス内から利用できる。 |
クラスにはprotectedとprivateは指定できないことに気を付けましょう。
アクセス修飾子の種類と例
では実際にコード上はどのように書くかを説明していきます。
public
クラス、コンストラクタ、変数、メソッドの全てに指定できます。
package packA;
public class Sample { //クラスで指定可能
public Sample{} //コンストラクタで指定可能
public int mum; //変数で指定可能
public void dispSample(){} //メソッドで指定可能
}
publicを指定するとどのクラスからでも利用可能です。
package packB; //パッケージは異なっても良い
import packA.Sample;
public class Test {
Sample sample = new Sample(); //コンストラクタにアクセス可能
sample.mum = 0; //メンバ変数にアクセス可能
sample.dispSample(); //メソッドにアクセス可能
}
publicは4つの中では一番公開範囲が広く、どこからでもアクセスができます。
protected
コンストラクタ、変数、メソッドに指定できます。クラスには指定できません。
package packA;
//protected class Sample { //クラスに指定不可
public class Sample {
protected Sample{} //コンストラクタで指定可能
protected int mum; //メンバ変数で指定可能
protected void dispSample(){} //メソッドで指定可能
}
protectedを指定すると、このクラスを継承したクラスや同一パッケージ内のクラスから利用が可能です。
こちらはNGな例です。
package packB; //パッケージは異なっているとアクセス不可
import packA.Sample;
public class Test1 {
//Sample sample = new Sample(); //コンストラクタにアクセス不可
//sample.mum = 0; //メンバ変数にアクセス不可
//sample.dispSample(); //メソッドにアクセス不可
}
OKな例です。
package packA; //パッケージが同一
public class Test2 {
Sample sample = new Sample(); //コンストラクタにアクセス可能
sample.mum = 0; //メンバ変数にアクセス可能
sample.dispSample(); //メソッドにアクセス可能
}
package packB;
import packA.Sample;
public class Test3 extends Sample { //protectedを指定したクラスを継承したクラスはアクセス可能
Sample sample = new Sample(); //コンストラクタにアクセス可能
sample.mum = 0; //メンバ変数にアクセス可能
sample.dispSample(); //メソッドにアクセス可能
}
同一パッケージ内からに加えて、異なったパッケージでも継承したクラスなら利用ができます。
しかし、一般的なシステム開発では利用することはめったにありません。
デフォルト(指定なし)
クラス、コンストラクタ、変数、メソッドの全てに指定できます。
package packA; //同一パッケージ内はアクセス可能
class Sample { //クラスで指定可能
Sample{} //コンストラクタで指定可能
int mum; //メンバ変数で指定可能
void dispSample(){} //メソッドで指定可能
}
同一のパッケージ内のクラスからのみ利用が可能なので、パッケージが違うと利用ができなくなります。
package packA; //同一パッケージ内はアクセス可能
public class Test {
//Sample sample = new Sample(); //コンストラクタにアクセス可能
//sample.mum = 0; //メンバ変数にアクセス可能
//sample.dispSample(); //メソッドにアクセス可能
}
デフォルトは同一パッケージ内のクラスからのみ利用したい場合に指定します。
パッケージごとに機能をまとめると思うのですが、そのクラス間でどうしても呼び出す必要があるときに指定します。
private
コンストラクタ、変数、メソッドに指定できます。クラスには指定できません。
package packA;
//private class Sample { //クラスで指定不可(エラーになる)
class Sample {
private Sample{} //コンストラクタで指定可能
private int mum; //メンバ変数で指定可能
private void dispSample(){} //メソッドで指定可能
}
同一のクラス内からのみアクセス可能なので、他のクラスからは一切アクセスできません。
package packB; //異なるパッケージ
import packA.Sample;
public class Test1 {
Sample sample = new Sample(); //アクセス不可、インスタンスも生成不可
sample.mum = 0; //変数にアクセス不可
sample.dispSample(); //メソッドにアクセス不可
}
アクセスするには同一パッケージからアクセスする必要があります。
package packA; //同一パッケージ
public class Test2 {
Sample sample = new Sample(); //アクセス可能(コンストラクタは生成不可)
sample.mum = 0; //変数にアクセス可能
sample.dispSample(); //メソッドにアクセス可能
}
4つの中では一番公開範囲が狭いです。
コンストラクタprivateを指定するとnewによるインスタンス生成ができなくなります。
メソッドやメンバ変数にprivateを指定するとオーバーライドができなくなります。
上記の例は極端なので正しい使い方はこの先の書き方を見てください!
アクセス修飾子を使い分けるメリット
アクセス修飾子は4つありますが、自分でクラスやメソッドを記載する場合は基本的にpublicとprivateの2つを使うことが多いです。
なぜならprotectedとデフォルトでは同一パッケージ内からのアクセスができるようになりますが、同一のパッケージ内だけ使いたいケースがあまりなく、あとから他のパッケージから呼び出せるように修正するときのコストが多くなってしまうからです。
そのため他クラスから利用する想定の場合はpublic、クラス内からしか利用しない場合はprivateを指定します。
ではpublicとprivateの使い分けについてです。
publicとprivateを使い分ける一番わかりやすい例はカプセル化にあります。
カプセル化とは、こちらのサイトではこのように説明されています。
カプセル化とは、オブジェクト指向プログラミングにおいて、互いに関連するデータの集合とそれらに対する操作をオブジェクトとして一つの単位にまとめ、外部に対して必要な情報や手続きのみを提供すること。外から直に参照や操作をする必要のない内部の状態や構造は秘匿される。
このようにカプセル化では情報や手続きを外部に提供するのですが、外から参照や操作をされたくないものは公開しない必要があります。
この「外から参照や操作をされたくない」と言うのを実現するのがprivate修飾子です。
メンバ変数にprivateを指定すると外部から参照も更新もできません。
他のクラスから変数の値が必要になる場合はpublicを指定したメソッドを通して受け渡しを行います。
カプセル化のコードの例はこちらです。
package model;
public class User{
private int id; //①privateで変数はアクセス不可とする。
private String name; //①privateで変数はアクセス不可とする。
public String getId(){ //②メソッドを通してidを返却する。
return this.id;
}
public String setUser(int id){ //③メソッドを通してidを設定。
this.id = id;
}
public String getName(){ //②メソッドを通してnameを返却する。
return this.name;
}
public String setName(String name){ //③メソッドを通してnameを設定。
this.name = name;
}
}
こちらはユーザ情報を保持するUserクラスです。
変数にidとnameを持っています。
①まずカプセル化は内部で持つメンバ変数は直接外部から参照、変更できないようにprivateを指定します。
②一般的にセッターと呼ばれるset〇〇のメソッドをpublicで定義し、privateの変数の値を設定するメソッドを作成します。
③一般的にゲッターと呼ばれるget〇〇のメソッドをpublicで定義し、privateの変数の値を取得するメソッドを作成します。
これによりidのようなメンバ変数はprivateで指定しているので外部から参照や変更ができないのですが、同一クラス内のpublicで指定したゲッター、セッターを利用することにより値の受け渡しができます。
ただし、このクラス自体は外部から利用させるためにクラス自体はpublicを指定します。
また、セッターではなくコンストラクタで値を設定することもありますが、今回はセッターで説明をします。
使い方
package app;
import model.User;
public class Test {
public static void main(){
int testId = 12345;
String testName = "まさき";
User user = new User(); //Userクラスのインスタンス生成
user.setId(testId); //OK
user.setName(testName); //OK
//user.id = testId; //直接アクセスはNG
//user.name = testName; //直接アクセスはNG
int resultId = user.getId(); //OK
String resultName = user.getName(); //OK
//int resultId = user.id; //直接アクセスはNG
//String resultName = user.name; //直接アクセスはNG
}
}
最初は少し回りくどく感じますがこのように記載することで変数が予期せぬ値に更新されるのを防ぐことや、機能をクラスごとにまとめることができます。
結果的に可読性や保守性が高くなりバグを防ぐことにつながるのです。
以上がアクセス修飾子の違いや使い方でした。
最後まで読んで頂きありがとうございます!