Override

Javaの用語のなかでオーバーロードとオーバーライドは違う概念を指すのだ、という事を知らなかったり、誤って憶えていたりする人が案外多いようです。オーバーロード(overload)というのは、同じクラスの同じ名前のメソッドで、引数並びが違うものを定義することで、オーバーライド(override)というのは、サブクラスが親クラスのメソッドやフィールドを再定義して置き換えてしまうことを指します。

オーバーロードはちょっと横に置いておいて、オーバーライドの話をしましょう。サブクラス側で親クラスのメソッドをオーバーライドする場合(そういう場合しか無いわけですが)、いくつかの制約があります。

  1. 親クラスのメソッドと同じ引数並びであること。
  2. 親クラスのメソッドと同じ戻り値の型であること。
  3. 親クラスのメソッドと同じ例外、あるいはそのサブクラスの例外しかthrowできない。
  4. 親クラスのメソッドより狭いスコープのアクセス修飾子は使えない。
  5. 親クラスのfinal指定されたメソッドはオーバーライドできない。
  6. 親クラスのpublic、protected、および指定なし(パッケージスコープ)のメソッドしかオーバーライドできない。

最初の3つはオーバーライドメソッドの書き方といってもよいので、問題ないでしょう。より狭いスコープを指定できないという制約は、例えば親クラスではpublicとしているメソッドをprivate指定してオーバーライドすることはできない、という意味です。逆にいうと親クラスでprotected指定されたメソッドをサブクラスでpublic指定してオーバーライドメソッドを作成することは許されています。final指定されたメソッドがオーバーライドできないのは、それがfinalの意味だからあたりまえです。

最後の制約は要するにprivateメソッドがオーバーライドできない、という意味です。これはprivateという修飾子はそのクラスの外(サブクラスを含め)からのアクセスを許さない、という意味から当然といえます。もっとも親クラスのprivate指定されたメソッドはサブクラスからは見えないので、全く同じ名前、引数並び、戻り値をもつメソッドをサブクラス側で定義することはできます。これはオーバーライドとは呼びません(superで親側のメソッドのアクセスができない)。

メソッドのオーバーライドは解りやすいのですが、フィールドのオーバーライドというのは少しややこしいです(でも可能です)。フィールドはオブジェクト毎に領域が必要になるので、コンパイル時にそのためのサイズが計算されています。このためサブクラス側のオーバーライドしたフィールドは親クラスのフィールドを遮る事はしても、上書きしてしまう訳ではありません。例えば、

public class SuperClass {
	public String myName = "SuperClassField";
	public void method() {
		System.out.println("SuperClassMethod and " + myName);
	}
}

public class SubClass extends SuperClass {
	public String myName = "SubClassField";
	public void method() {
		System.out.println("SubClassMethod and " + myName);
	}
}
という定義があったとして、
		SubClass child = new SubClass();
		child.method();
		System.out.println(child.myName);
		SuperClass parent = child;
		parent.method();
		System.out.println(parent.myName);
を実行した場合、
SubClassMethod and SubClassField
SubClassField
SubClassMethod and SubClassField
SuperClassField
が出力されます。

child.method()が呼び出された時に、オーバーライドしたmethod()が呼ばれ、それはサブクラスのフィールドを参照しますので、SubClassMethod and SubClassFieldが出力され、またchild.myNameはSubClassFieldになります。

一方これをSuperClassとして参照した場合にparent.method()が呼び出されても、オブジェクトそのものはSubClassオブジェクトですので、オーバーライドしているSubClassのmethod()が呼び出され、またオーバーライドしているサブクラスのフィールドが参照され、SubClassMethod and SubClassFieldが出力されます。しかしparent.myNameの方は親クラス側にちゃんと領域が確保されているため、SuperClassFieldが出力されます。

特別な理由が無い限りフィールドをオーバーライドして使用することは無いでしょうが、もし使用した場合はオブジェクトがどのクラスの参照として使われるのかに十分留意する必要があります。というより問題を少なくするためには、フィールドを外部からアクセスする場合には、専用のメソッドを用意しておくのが良い、ということでしょう。


人材開発室 PoisonSoft