概要
Javaのアクセス修飾子の一つに、protectedというものがあります。protectedについて「同一パッケージ内およびサブクラスからのみアクセス可能」というような説明を聞いたことがないでしょうか。このような理解だと若干の誤解が生じるかもしれないという話をします。
同一パッケージ内からのアクセスについては特に混乱する要素はないと思うので、サブクラスからのアクセスについてのみ言及します。
対象読者
Java初心者
環境
Java11で動作確認を行いましたが、内容としては古いバージョンでも特に変わらないと思います。
うまくいかない例
「サブクラスからアクセス可能」という理解だと、下記のサンプルコードは動作すると思えますが、実際にはコンパイルエラーになります。
(ネーミングセンスについてはご容赦願います…)
package test.sp;
public class Super {
protected void method() {
System.out.println("Super#method() called.");
}
}
package test.sub;
import test.sp.Super;
public class Sub extends Super {
public static void main(String[] args) {
Sub sub = new Sub();
sub.run();
}
private void run() {
Super sp = new Super();
sp.method(); // エラー: method()はSuperでprotectedアクセスされます
}
}
protectedで修飾されたmethod()メソッドを持つSuperクラスと、そのSuperクラスを継承したSubクラスがあります。
SuperクラスとSubクラスはそれぞれ別のパッケージにありますが、SubクラスはSuperクラスを継承している(SubクラスはSuperクラスのサブクラスである)ため、Subクラスからmethod()メソッドを呼び出すことは可能であるように思えます。しかし、上記のコードだとコンパイルエラーとなってしまいます。
うまくいく例
package test.sp;
public class Super {
protected void method() {
System.out.println("Super#method() called.");
}
}
package test.sub;
import test.sp.Super;
public class Sub extends Super {
public static void main(String[] args) {
Sub sub = new Sub();
sub.run();
}
private void run() {
// (1)
Sub sub = new Sub();
sub.method();
// (2)
this.method();
// (3)
method();
// (4)
super.method();
}
}
今度はうまくいく例ですが、SuperクラスおよびSuperクラスとSubクラスとの関係は変わらず、Subクラスからの呼び出し方法だけ変わっています。(1)から(4)までありますが、どれもコンパイルが通り、実行時にも特にエラーは発生せずに動作しました。
// (1)
Sub sub = new Sub();
sub.method();
(1)はSubクラスのインスタンスを明示的に生成して、そのインスタンスのmethod()を呼び出しています。Superクラスのインスタンスではコンパイルエラーになりましたが、Subクラスのインスタンスであれば問題ないようです。
// (2)
this.method();
// (3)
method();
(2)はthisでの呼び出しです。これも動作します。(3)はthisの記述を省略しているだけで、(2)と実質同じです。thisは自身のインスタンスを指すわけなので、Subクラスのインスタンスのmethod()を呼んでいるという意味では、(1)〜(3)のいずれも同じです。
// (4)
super.method();
(1)〜(3)と少し異なるのが(4)です。(4)はsuperのmethod()を呼び出していますが、これも動作します。superは(自身と関連づけられている)スーパークラスのインスタンスを指します。Superクラスのインスタンスを明示的に生成して、そのインスタンスのmethod()を呼び出した際はコンパイルエラーとなったのに、superであれば問題ないのですね。
いずれもSuperクラスのインスタンスという点では変わらないので同じ挙動になると思っていたのですが、ちょっと意外な結果となりました。
まとめ
protectedで修飾されたメソッドについて、別パッケージで定義されたサブクラスからの可視性は下記のようになります。
- サブクラス内で、newで明示的に生成されたサブクラス自身のインスタンスから呼び出せる
- サブクラス内のthisから呼び出せる。thisの記述を省略して呼び出す場合も同様
- サブクラス内のsuperから呼び出せる
- サブクラス内で、newで明示的に生成されたスーパークラスのインスタンスからは呼び出せない
- protectedの可視性について「同一パッケージ内およびサブクラスからのみアクセス可能」と理解していると、上記の呼び出せないパターンについて呼び出せると勘違いする可能性がある
蛇足
同一パッケージ内からのみ呼び出し可能にしたいという意図でprotectedで修飾されていると思われるメソッドを見かけることがありますが、この場合はprotectedではなくパッケージプライベート(修飾子なし)を使うべきです。
protectedって個人的にはあまり使いません。(使い方を理解してないのかもしれませんが…)