メインコンテンツへスキップ

Javaのprotectedの意味

NOTE
この記事は最終更新日から1年以上が経過しています。内容が古くなっている可能性があります。

Javaのアクセス修飾子protectedで少しハマったので、調べた内容をメモしておきます。

通常、protectedと指定されたフィールドやメソッドは「同一package内と、そのクラスを継承したサブクラス内からアクセスできる」と説明されます。私自身も同様の理解だったのですが、次のようなケースで予想外の挙動となりました。

クラスBaseとChildが別のpackageに属する場合、

package base;

public class Base {

	protected void baseMethod() {
		System.out.println("Base method.");
	}

}
package child;

import base.Base;

public class Child extends Base {

	public void childMethod(Base base) {
		base.baseMethod();	// NG
	}

}

Childクラスの中でBaseクラスの引数を受け取ってbaseMethod()を呼び出そうとすると、“The method baseMethod() from the type Base is not visible"と怒られてしまうのです。

実は、Javaのprotected指定の意味は、正確には「同一パッケージ内と、そのクラスを継承したサブクラス内から、 そのサブクラスへの参照を通して アクセスできる」となります。つまり、Childクラスの中から呼ぶことができるbaseMethod()は、あくまでもChildクラス(またはChildクラスのサブクラス)のbaseMethod()であり、Baseクラスのそれではないのです。

例えば、次のケースではbaseMethod()を呼ぶことができます。

package child;

import base.Base;

public class Child extends Base {

	public void childMethod(Child child) {
		this.baseMethod();	// OK
		child.baseMethod();	// OK
	}

}

確かに、親クラスのprotectedフィールドをサブクラスから直接書き換えられると問題があるかもしれませんが、そういうフィールドはprivateにすればよい(というか、そうするべき)し、メソッド呼び出しはポリモーフィズムにすることができるので、これは少々余計な制限であるように思います。

どうしてもChildクラスの中からBaseクラスの参照を使ってbaseMethod()を呼び出したい場合は、

package base;

public class Base {

	protected void baseMethod() {
		System.out.println("Base method.");
	}

	protected void invokeBaseMethod(Base base) {
		base.baseMethod();
	}

}
package child;

import base.Base;

public class Child extends Base {

	public void childMethod(Base base) {
		invokeBaseMethod(base);
	}

}

こんなふうにするしか手はなさそうです。

関連記事

JNIでenumを扱う

Java 5.0から導入されたJavaのenumですが、JNIを経由したnativeメソッド内ではどのように扱えばよいのでしょうか?これについては、JNI仕様にも明記されていないようです。必要があったので、ちょっと調べてみました。