【本サイトではGoogleアドセンス、または、アフィリエイト広告を利用しています。】
アクセス修飾子とスコープ
これまでJavaの構文についていろいろとお話をしてきましたが、
1つ先延ばしにしてきたことがあります。
それは、アクセス修飾子についてです。
クラスやメソッドを定義する時に「public」というワードをつけていたのを
覚えているでしょうか?これもアクセス修飾子の1つです。
クラスやオブジェクト、パッケージの概念がわかっていないと、
この内容を理解することが難しいので後回しにしていたのです。
もしまだこれらの内容が理解できていない方は
先にこちらを読んでいただくとより理解が深まると思います。
アクセス修飾子とは、プログラムを扱うことのできる範囲を定義したものです。
また、プログラムを扱うことができる範囲のことをJavaではスコープと呼びます。
そして、プログラムを扱うとは、参照する、呼び出すということです。
つまり、他のプログラムから呼び出して使うことができるのか、できないのか、
アクセス修飾子で制限をかけるということです。
アクセス修飾子の種類
アクセス修飾子には以下の種類があります。
クラス、メソッド、フィールドにそれぞれ付加することができますが、
定義するものによって付加できるかどうかが変わってきます。
また「付加しない」こともでき、付加した場合とスコープが違ってきます。
アクセス修飾子 | スコープ(参照範囲) | 付加できるもの |
---|---|---|
public | どこからでも参照可能。 | クラス、メソッド、フィールド |
private | 同一クラスからのみ参照可能。 | メソッド、フィールド |
protected | 同一パッケージ、または、 サブクラスからのみ参照可能。 | メソッド、フィールド |
なし | 同一パッケージからのみ参照可能。 サブクラスからは参照不可。 | クラス、メソッド、フィールド |
アクセス修飾子の使い方(サンプルコード)
アクセス修飾子を変えることで動きにどのような違いが出てくるか、
サンプルコードで見ていきましょう。
まずは以下の構成でプログラムを準備します。
Exampleはメイン処理を実行するクラスです。
各クラスをインスタンス化して動作させます。
package example;
import robot.Robot;
import robot.SubRobot;
// クラス:メイン処理実行用
public class Example {
// メイン処理
public static void main(String[] args) {
// ロボット作成
Robot robo = new Robot();
// 能力の設定
robo.energy = 200;
// あいさつ
robo.greeting();
// サブロボット作成
SubRobot subRobot = new SubRobot();
// 能力の設定
subRobot.energy = 50;
subRobot.name = "サブチャン";
// トーク
subRobot.talking();
}
}
スーパークラスとしてRobotを定義しました。
package robot;
//クラス:ロボット
public class Robot {
// フィールド:エネルギー
public int energy;
// メソッド:あいさつ
public void greeting() {
System.out.println("ヨロシクオネガイシマス!エネルギーは【" + energy + "】デス!");
}
}
続いて、RobotのサブクラスとしてSubRobotクラスを定義しました。
トークを実行する中で、スーパークラスから継承したあいさつも同時に行います。
package robot;
public class SubRobot extends Robot {
// フィールド:名前
public String name;
// メソッド:トーク
public void talking() {
this.greeting();
System.out.println("「" + name + "」と呼んでクダサイ。");
}
}
ここで、Robotクラス内のアクセス修飾子を書き換えると、
動作にどのような変化が出てくるのか見てみましょう。
public(パブリック)
例はすべてpublicで指定していますので、まずはこのまま実行してみましょう。
ヨロシクオネガイシマス!エネルギーは【200】デス!
ヨロシクオネガイシマス!エネルギーは【50】デス!
「サブチャン」と呼んでクダサイ。
うまく実行できました。
publicは、どこからでもプログラムを参照できるので、
異なるパッケージにあるExampleクラスからでも
Robotのメンバを参照することができます。
このように、publicは制約を気にしなくていいので
使い勝手が良く便利ではありますが、どこからでもアクセスできる分、
本来呼び出してはいけないプログラムを呼び出してしまったり、
フィールドの値を誤って書き換えてしまったりするリスクもあります。
publicを指定する際は、誤った参照によるバグの危険性を考慮するようにしましょう。
物事で例えるなら、publicは、ネット上に公開する資料のようなイメージです。
private(プライベート)
では、Robotクラスのメンバをすべてprivateにしてみます。
package robot;
//クラス:ロボット
public class Robot {
// フィールド:エネルギー
private int energy;
// メソッド:あいさつ
private void greeting() {
System.out.println("ヨロシクオネガイシマス!エネルギーは【" + energy + "】デス!");
}
}
実行すると以下のようなエラーが出ました。
Robotクラスのフィールドとメソッドが不可視、
つまり、「参照できないから使えないよ」というエラーになります。
Exampleクラス内を見ると、該当部分にエラーが出ています。
サブクラスのSubRobotにもエラーが出ています。
privateではクラス内に参照範囲が絞られる為、
他のクラスから参照している部分はすべてエラーになります。
このように、privateを使えば、
強制的にクラス内で処理を完結させることをルール化できるので、
外からの参照を考慮して書いていないプログラムに使うようにしましょう。
また、フィールドやメソッドを定義した時点で、
外部からそれらを呼び出して使うという意図がない場合は、
品質面を考慮して、まずは参照範囲を最小限まで絞った状態で定義し、
必要に応じて範囲を変えることが望ましいと私は考えます。
ということで、クラス内にメンバを作成する時はまずprivateで作成し、
必要に応じてprotectedやpublicに参照範囲を広げるようにしましょう。
物事で例えると、privateは、誰にも見せない自分だけの日記というイメージです。
protected(プロテクテッド)
次に、Robotクラスのメンバをすべてprotectedにしてみます。
package robot;
//クラス:ロボット
public class Robot {
// フィールド:エネルギー
protected int energy;
// メソッド:あいさつ
protected void greeting() {
System.out.println("ヨロシクオネガイシマス!エネルギーは【" + energy + "】デス!");
}
}
実行すると、先ほどと同じく、
Robotクラスのメンバが参照できないというエラーが出ました。
今度は、Exampleクラスのみにエラーが出ています。
privateの時はサブクラスSubRobotにもエラーが出ていましたが、
protectedにしたことでSubRobot内のエラーはなくなりました。
これは、privateからprotectedにすることで、スコープが広がったからです。
また、プログラムの構成を以下のように変更して、
すべて同一パッケージ内にクラスを置くと、エラーは一切出なくなります。
このように、protectedでは、自身のサブクラスや同一パッケージ内からの
アクセスは許可するという特徴を持ちます。これは見方を変えると、
同系統、同種のクラスからのみ参照できるという特徴があると言えます。
まったく関係性のないプログラムからの利用を強制的に排除したい時などに使いましょう。
物事で例えるなら、protectedは、社外秘の資料のようなイメージです。
指定なし(デフォルト)
では最後に、アクセス修飾子を指定しないとどうなるんでしょう?
さっそくやってみましょう!
メンバだけでなく、クラス自体の修飾子も「指定なし」にしてみます。
package robot;
//クラス:ロボット
class Robot {
// フィールド:エネルギー
int energy;
// メソッド:あいさつ
void greeting() {
System.out.println("ヨロシクオネガイシマス!エネルギーは【" + energy + "】デス!");
}
}
実行すると、予想通りエラーが出ました。
Robotクラスなんて型はないよ!というエラーです。
Exampleクラス内です。処理だけでなく、import文にもエラーが出ていますが、
これは、異なるパッケージにあるクラスは参照できないという特性によるものです。
また、この状態から、SubRobotクラスをexampleパッケージに移動して、
Robotクラスをpublicに指定すると、
Robotをpublicにしたので、SubRobotをサブクラスとして定義できますが、
Robotのメソッドgreetingにはアクセス修飾子の指定がないので参照できずエラーになります。
どうでしょう?ややこしいですよね?
継承はできるのに、メソッドは呼び出せないなんて、どういった状況で使えるんでしょう?
おわかりのように、デフォルト参照を使うメリットは特にありません。
ということで、クラスやメンバを定義する際には、
必ずアクセス修飾子を定義するようにしましょう!
まとめ
Javaのアクセス修飾子について以下説明しました。
・アクセス修飾子の概念と種類
・publicの使用例
・privateの使用例
・protectedの使用例
・定義なしの使用例
チームで仕事をする時などは、アクセス修飾子をうまく使うことで、
使わせたくないプログラムを制御することができます。
ルールとして決めるだけでなく、プログラム上からも呼び出せないようにしておくことで
より品質の高いプログラミングを実現できるので、使い方をぜひマスターしておきましょう!