第 2 章 Java 言語の基本的な文法 I 5
3.24 PrivateFieldExample.java (private field へ直接アクセス )
クラスのアクセス制限に「private」と「protected」が選択できない以外は、いずれのメンバーも全ての修飾子を使 用することができます。ただ、ここでもまたちょっと嘘があって、「内部クラス(inner class)」というクラスでは全て の修飾子が利用できます。「内部クラス」は後で説明しますが、この授業では触れないかな。
メンバーにアクセス修飾子を付けるには、型宣言の際に先頭に書きます。例えば、private int x = 123; や public static double[] x = new double[100];なんて感じですね。以下で1つずつ見ていきましょう。
3.3.2 private
privateは、最も制限の強いアクセス修飾子で、コンストラクタ・フィールド・メソッドに付けることが出来ます。
この修飾子が付いたメンバーは、含まれるクラスの外からは直接アクセスすることができません。アクセスできるの は、そのクラス内部のメンバーからのみです。直接アクセスするとは、次のような処理を言います。
ソースコード3.24 PrivateFieldExample.java (private fieldへ直接アクセス)
ソースコード3.25 (改)PrivateFieldExample.java (private fieldへ直接アクセス) package section0303;
public class PrivateFieldExample {
public static void main(String[] args) { Circle circle = new Circle( 10.0 );
double radius = circle.getRadius();
double area = circle.getArea();
System.out.printf("半径 %f の円の面積は、%f%n", radius, area );
} }
class Circle { // 円のクラス private double radius; // 半径
Circle( double radius ) { // コンストラクタ this.radius = radius;
}
double getRadius() { // 半径を返すゲッター return this.radius;
}
double getArea() { // 面積を返すメソッド return radius * radius * Math.PI;
} }
ゲッターによるアクセスのもう一つの利点は、今、Circle クラスを設計している葛飾くんが、半径の変数を
double hankei; と英語から日本語に替えようと思ったとします。このとき、直接のアクセスを許していると、
PrivateFieldExample クラスを作っている野田さんに、circle.radius; から circle.hankei;への変更を連絡 しなければいけません。radius への直接のアクセスを不許可として、野田さんがcircle.getRadius()を使うこと になれば、葛飾くんがゲッターgetRadius() 内でreturn hankei;と変更しても、野田さんには影響なく済むわけ です。
問題 3.3.1. 上のプログラムのCircleクラスを同じパッケージ section0303内の別のファイルCircle.javaに分 離しなさい。その場合、クラス・コンストラクタ・メソッドをこのまま(package-privateのまま)利用できることを確 かめなさい。
コンストラクタのprivate化
ところで、 コンストラクタにprivateを付けると、どうなるでしょう。この場合もそのクラスの外部からのコンス トラクタの使用が出来なくなります。つまりこのコンストラクタを用いてインスタンス作成ができなくなる。えっ?
じゃ、このコンストラクタ何に使うのでしょう?
privateなコンストラクタはインスタンスを作らせないためにあるのです!?例えば、三角関数のcos()メソッド
などが入っているMathクラスはクラスフィールドとクラスメソッドのみからなるクラスです。数学関数の集まりとし て定義されたクラスで、Math クラスのインスタンスを作る!というようには定義されていません。というか、させま せん。従って、Mathクラスにはコンストラクタがありません。
しかし、コンストラクタを書かないでおくと、先に触れたようにコンパイラは「デフォルトコンストラクタ」を自動的 に作ってしまいます。そこで、わざとMathクラスの内部にはprivateなデフォルトコンストラクタが入っています。
すると、既にデフォルトコンストラクタがあるので、自動的な作成が起きない上に、外部から Mathクラスのインスタ ンスを作ろうとしても、作れないよ!というわけです。他にも privateなコンストラクタの利用価値に「シングルト ン特性の強制」(あるクラスのインスタンスを1つだけ作るが2つ以上は作りたくないとき)に利用します。これはこの テキストの範囲外なので、ここでは省略!
一方、クラスにprivateを付けられないのは、外部からクラス自体が見えないのではクラスの存在意味がなくなり ますから当然ですかね。
3.3.3 package-private
アクセス修飾子が書かれていない(省略されている)状況は、正確な用語では「デフォルトアクセス」のレベルと言 うそうですが、一般的には「パッケージプライベート (package-private)」で通ります。アクセス修飾子が省略され ている場合、そのメンバーは、そのクラスが含まれるパッケージ内のどのクラスからもアクセスすることができます が、パッケージの外からは直接アクセスできません。パッケージというグループ化をする目的からも、パッケージの内 と外を分けるこのアクセス修飾子は意味がありますね。同じ名前のクラスを複数作りたいけれど、クラスに付けられる アクセス修飾子は package-privateと publicのみです。それらクラスにいずれもpublicを付けると、パッケージ を超えて重複エラーになるので、常にパッケージ名を付けながら区別しないといけなくなり、結構面倒です。そこで、
package-privateにしてそれぞれ異なるパッケージ内に置けば、面倒が無くなります。セキュリティの観点からも、可
能な限り publicにせずにpackage-privateにするよう、推奨されています。
3.3.4 protected
protectedは、2番目に制限の弱いアクセス修飾子です(package-privateより弱い)。この修飾子の付いたメンバー は、含まれるパッケージ内のどのクラスからも直接アクセスできる上に、パッケージ外でもそのクラスを「継承」した クラス (サブクラスと言う)から、アクセスすることができるというものです。「継承」や「サブクラス」についてはこ の後で説明しますが、要は、そのクラスを拡張しているなら外部からでも使って良いよ!という感じですかね。
この授業のレベルではprotectedを付ける機会はそれほど多くはありませんが、package-privateだったメンバー をprotectedに制限を緩めると、設計者が管理できない他人のプログラム内で(継承されることで)勝手に利用され るようになるので、その差は大きいと言えます。
3.3.5 public
publicは、最も制限の弱いアクセス修飾子で、クラスやパッケージに関わらず、どこからでも直接アクセスできま
す。事実上の制限なしです。
4 つのアクセス修飾子は、メンバーごとに独立に与えられますが、例えば、package-private なクラス A の中に publicなメソッドa()を置くとどうなるでしょう?
クラスAがそのパッケージの外からアクセスできないために、せっかくの publicが生かせず、パッケージの外から a()を利用することができません。つまり、入れ物であるクラスのアクセス修飾子に対し、そのメンバーのアクセス制 限は同等かより強くなければいけません。
問題 3.3.2. 次のプログラムにおいて、どの行でコンパイルエラーが表示されるでしょう。
package section0303;
public class Problem0332 {
public static void main(String[] args) { A a1 = new A();
a1.setValue( 10 );
System.out.println( a1.value );
A a2 = new A( 10 );
System.out.println( a2.getValue() );
} }
class A {
private int value;
A( int value ) { this.value = value;
}
void setValue( int value ) { this.value = value;
}
int getValue() { return this.value;
} }
問題 3.3.3. 問題 3.3.2で文法エラーが表示されないように修正したとし、その上で、クラス A を別のパッケージ
section0303a に移動したとします(Eclipse上でプログラム移動を行うと、プログラム内にimport文が自動作成さ れます)。 新たに表示される文法エラーの表示理由を考察して、プログラムを修正しなさい。
アクセス制限をpublicにするということは、安易に削除したりアクセス制限を強めたりできない、という意味にな ります。何故なら、パッケージや継承を超えてそのクラスやメソッドを利用できるようにした以上は、多くの人がそれ を使っている可能性があるからで、それらの人たちへの責任が出てくるわけです。従って、極力publicは止めましょ う!となるんですね。それに、メソッド名をちょっと間違って書いたために、思いもしない大昔に作ったpublic メ ソッドが勝手に動いていた、なんてこともありえるからね。
問題 3.3.4. 4種類のアクセス修飾子をその制限の強い順に左から右に(最も強いアクセス修飾子を左に、弱い修飾子
を右に)並べて書きなさい。なお、アクセス修飾子を省略する場合の修飾子は「省略する」と書きなさい。
次に、そのうちでクラスに付けることの出来ないものを選び並べなさい。
す。
例えば、「自動車」についてのプログラムを作ろうと思います。「セダン」「トラック」「スポーツカー」それぞれをオブ ジェクトとして定義するとき、いずれもが共通に持つフィールドやメソッドを重複して宣言するのは無駄ですし、どれ か1つを修正したとき、他のオブジェクトのものも修正しなければならなくなって、面倒な上にミスさえ起こりやすく なります。そこで、「自動車」という共通概念のオブジェクトを作って共通のフィールドやメソッドはそれに持っても らい、それぞれの車種特有の属性値や機能はそれぞれのオブジェクトに追加・補足として与えるわけです。
「自動車」クラスのような継承される側のクラスを「スーパークラス(super class)」と言い、「トラック」クラスのよ うな継承する側のクラスを「サブクラス(sub class)」と呼びます。数学の言葉では、(部分集合をsubsetと言うよう に)サブという単語が付くと部分的なものを指すので、サブクラスの方が属性値や機能が少なそうに思えますが、Java の世界では逆で、サブクラスに当たる方が一般に属性値や機能が増えます。
以下の例では、「学生」クラスと「教授」クラスを定義しています。その際に、それらの上位概念の「人」クラスを 作って、それを継承するプログラムになっています。(ちょっと無理っぽい例ですけどね)
ソースコード3.26 InheritanceExample.java (継承関係)