クラスの拡張

  1. クラスの拡張
  2. 拡張したクラスのコンストラクタ
  3. メソッドのオーバライドとフィールドの隠蔽
  4. メソッドとクラスを final 宣言する
  5. Object クラス
  6. 抽象クラスとオブジェクト
  7. 拡張されうるクラスを設計する

  1. クラスの拡張
  2. オブジェクト指向の便利な点に、クラスの拡張機能があります。 クラスを拡張することによって、元のコードを再利用しつつ機能を拡張できます。

    拡張したクラスは、 元のクラスのサブクラス、 または拡張クラスと呼ばれます。
    他方、元のクラスはスーパクラスと呼ばれます。

    拡張したクラス

    extends 節の宣言の有無にかかわらず、すべてのクラスは拡張クラスです。 なぜなら java のクラスは、Object クラスを暗黙で拡張しています。 すなわち、Object クラスは階層構造における全クラスのルートです。 Object クラスはすべてのオブジェクトがもつべきメソッドを定義しています。

  3. 拡張したクラスのコンストラクタ
  4. クラスを拡張するとき、新しいクラスを呼び出すコンストラクタを、 スーパクラスのコンストラクタの中から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デフォルト値を代入 000
    1Y のコンストラクタ 0
    0
    2X のコンストラクタ 0
    0
    3X のフィールド初期化 0x00ff00
    4X のコンストラクタ実行 0x00ff00x00ff
    5Y のフィールド初期化 0x00ff0xff000x00ff
    6Y のコンストラクタ実行 0x00ff0xff000xffff

  5. メソッドのオーバライドとフィールドの隠蔽
  6. メソッドをオーバライドするとは、 スーパクラスによるメソッドの実体を、 新しいものに置きかえることです。 シグネチャは全く同じでなくてはなりません。 また 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

    super は、拡張クラスの static でない、すべてのメソッドから利用可能です。 フィールドをアクセスしたりメソッドを呼び出すとき、 super は現在のオブジェクトをスーパクラスのインスタンスとみなし、 そのオブジェクトに対する参照として動作します。

  7. メソッドとクラスを final 宣言する
  8. メソッドを final 宣言すると、 クラスを拡張してもそのメソッドをオーバライドできません。 クラス全体を final とすることもできます。

    メソッドを final とすることには、次の2つの効果があります。

  9. Object クラス
  10. すべてのクラスは 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 を見てみましょう。

  11. 抽象クラスとオブジェクト
  12. 抽象クラスを使うと、 一部のメソッドのみ実装しているようなクラスを宣言でき、 拡張クラスによって残りのメソッドの一部または全部を実装します。 このようなクラスを作るときは abstractと宣言します。 クラス中に実装されていないメソッドも abstract 宣言しなければなりません。

    抽象クラスとインタフェースの違い

    抽象クラスとインタフェースの違いを以下に示します。

    java.lang.Number は、抽象クラスで、java.lang.Integer は、 その実装の1つです。ソースを見てみましょう。

  13. 拡張されうるクラスを設計する
  14. 保護されている設計は、悪意のある使用をできなくすると同時に、 バグを減らす効果があります。





まとめ