拡張したクラスは、
元のクラスのサブクラス、
または拡張クラスと呼ばれます。
他方、元のクラスはスーパクラスと呼ばれます。
extends 節の宣言の有無にかかわらず、すべてのクラスは拡張クラスです。 なぜなら java のクラスは、Object クラスを暗黙で拡張しています。 すなわち、Object クラスは階層構造における全クラスのルートです。 Object クラスはすべてのオブジェクトがもつべきメソッドを定義しています。
クラスを拡張するとき、新しいクラスを呼び出すコンストラクタを、 スーパクラスのコンストラクタの中から1つ選ばなければなりません。
明示的なコンストラクタ呼び出しは、super() を用いることです。 もしスーパクラスのコンストラクタを明示的に呼び出さない場合は、 自動的に引数なしのスーパクラスのコンストラクタがよびだされます。 super() を用いる場合は、コンストラクタの初めの実行文でなければなりません。
オブジェクトが生成されると、全フィールドの型に応じたデフォルトの初期値
が設定されます。
すなわち数値型はゼロ、char は \u0000、
boolean は false、参照型は null が設定されます。
コンストラクタの実行には、次の3つの段階があります。
クラス X と クラス Y を見てみましょう。
class X { protected int xMask = 0x00ff; protected int fullMask ; public X() { fullMask = xMask; } public int mask(int orig) { return (orig & fullMask); } } class Y extends X { protected int yMask = 0xff00; public Y() { fullMask |= yMask; } }
初期化の様子は以下のとおりです。
No | 事 象 | xMask | yMask | fullMask |
---|---|---|---|---|
0 | デフォルト値を代入 | 0 | 0 | 0 |
1 | Y のコンストラクタ | 0 | 0 | |
2 | X のコンストラクタ | 0 | 0 | |
3 | X のフィールド初期化 | 0x00ff | 0 | 0 |
4 | X のコンストラクタ実行 | 0x00ff | 0 | 0x00ff |
5 | Y のフィールド初期化 | 0x00ff | 0xff00 | 0x00ff |
6 | Y のコンストラクタ実行 | 0x00ff | 0xff00 | 0xffff |
メソッドをオーバライドするとは、 スーパクラスによるメソッドの実体を、 新しいものに置きかえることです。 シグネチャは全く同じでなくてはなりません。 また static メソッドはオーバライドできません。
フィールドはオーバライドできませんが、隠すことは可能です。 スーパクラスと同じ名前のフィールドを定義すると、 スーパクラスのフィールドはありますが、 単にその名前をつかっただけでは、アクセスできません。
オブジェクトのメソッドを呼び出すと、
オブジェクトの実体の型のメソッドを呼び出します。
一方、フィールドをアクセスする場合は、
オブジェクトの参照の型のフィールドがアクセスされます。
クラス SuperShow と ExtendShow を見てみましょう。
class SuperShow{ public String str = "SuperStr"; public void show() { System.out.println("Super.show : " + str); } } class ExtendShow extends SuperShow { public String str = "ExtendStr"; public void show() { System.out.println("Extend.show : " + str); } public static void main(String[] args) { ExtendShow e = new ExtendShow(); e.print(); } void print() { ExtendShow ext = new ExtendShow(); SuperShow sup = ext; if (sup instanceof ExtendShow) System.out.println("sup is ExtendShow"); System.out.print("sup.show() ");sup.show(); System.out.print("ext.show() ");ext.show(); System.out.print("super.show() ");super.show(); System.out.print("this.show() ");this.show(); System.out.println("sup.str = " + sup.str); System.out.println("ext.str = " + ext.str); System.out.println("super.str = " + super.str); System.out.println("this.str = " + this.str); } }
osaka-67] java ExtendShow sup is ExtendShow sup.show() Extend.show : ExtendStr ext.show() Extend.show : ExtendStr super.show() Super.show : SuperStr this.show() Extend.show : ExtendStr sup.str = SuperStr ext.str = ExtendStr super.str = SuperStr this.str = ExtendStr
説明 |
上記プログラムについて簡単に説明します。
オブジェクトは ext 1つですが、ext と sup の2つの参照があります。 1つの参照は実際のタイプに対して、もう1つはスーパクラスへの参照です。 show メソッドに対しては、参照のタイプでなく、 オブジェクトの実際のタイプのメソッドを呼びます。 str フィールドに対しては、オブジェクトの実際のタイプではなく、 参照のクラスに属するフィールドをアクセスします。 実は、ExtendShow オブジェクトは同じ名前の str フィールドを2つ持っています。 1つは SuperShow から継承されたものですが、 それは ExtendShow 自身のもう1つの str で隠されています。 |
メソッドをオーバライドすることで、 既存のコードを再利用して拡張機能を与えることができますが、 フィールドの隠蔽に関しては、熟慮する必要があります。
super は、拡張クラスの static でない、すべてのメソッドから利用可能です。 フィールドをアクセスしたりメソッドを呼び出すとき、 super は現在のオブジェクトをスーパクラスのインスタンスとみなし、 そのオブジェクトに対する参照として動作します。
メソッドを final 宣言すると、 クラスを拡張してもそのメソッドをオーバライドできません。 クラス全体を final とすることもできます。
メソッドを final とすることには、次の2つの効果があります。
すべてのクラスは Object クラスを直接、間接に拡張します。 つまりすべてのクラスは Object クラスのメソッドを継承します。
Object クラスのメソッドは、 一般的なユーティリティとスレッドに関するものの2種類あります。
一般的なユーティリティには、以下のものがあります。
public boolean equals(Object obj) public int hashCode() protected Object clone() throws CloneNotSupportedException public final Class getClass() protected void finalize() throws Throwable
java Core API の java.lang.Object を見てみましょう。 |
抽象クラスを使うと、 一部のメソッドのみ実装しているようなクラスを宣言でき、 拡張クラスによって残りのメソッドの一部または全部を実装します。 このようなクラスを作るときは abstractと宣言します。 クラス中に実装されていないメソッドも abstract 宣言しなければなりません。
抽象クラスとインタフェースの違いを以下に示します。
抽象クラスは継承が必要で、動的にオブジェクトの型に合わせて、 適切なメソッドを選ばなければならないことを意味します。 この意味で、インタフェースを実装したものは、 クラスを拡張したものよりコストがかからないといえます。
java.lang.Number は、抽象クラスで、java.lang.Integer は、 その実装の1つです。ソースを見てみましょう。 |
保護されている設計は、悪意のある使用をできなくすると同時に、 バグを減らす効果があります。
まとめ |