Deep Side of Java〜Java 言語再入門 第2回 〜 Java 文法を中心に

オブジェクト指向に関する仕様

コンストラクタ






オブジェクト指向に関する仕様

コンストラクタ

さて、クラスには特別にコンストラクタと呼ばれるメソッドのようなものを定義できる。これはメソッドと似てはいるが、まったく別物であることに注意を喚起しておく。コンストラクタはオブジェクト指向言語に固有な、オブジェクトを生成したときに自動的に実行されて初期化などの役割を果たすものである(以前のC言語の模倣例では LikeObject_init という関数があるが、これはメソッドのように構造体の中に入れることができなかったことを思い出して欲しい)。

public class myObject {
        int x;
        public myObject( int x ) {
                this.x = x;
        }
        public myObject( ) {
                super();
        }
........
        myObject mo1 = new myObject();
        myObject mo2 = new myObject( 5 );

コンストラクタは戻り値のない、クラスと同名のメソッドのように宣言されたものである(戻り値は強いていえばそのクラスのオブジェクトである)。

new 演算子によって、新しくオブジェクトが作られるときに、そのオブジェクトを初期化するのに使われることは、この呼び出し例から明らかであろう。しかし、コンストラクタは継承と複雑な関係を持つものであり、「真に継承」されることはない。また、以下にまとめるように、メソッドとはさまざまな面で異なる。

コンストラクタの動作

コンストラクタはは疑似コードで書けば次のように動作する。

theObject o = malloc( sizeof(theObject) ); /* メモリの確保 */
..............  
コンストラクタ( o, args, theObject );

コンストラクタ( 確保された実体, 引数形式, クラス名 ) {
        if( そのクラスが、ルートクラスである Object である ) {
                /* なにもしない */
                return;
        }
        引数形式に合致するコンストラクタを選ぶ
        if( そのコンストラクタが this(...) で始まる ) {
                this(...) で示されるコンストラクタを適切な引数で呼び出す
        } else if( そのコンストラクタが super(...) で始まる ) {
                super() で示される親クラスのコンストラクタを適切な引数で呼び出す
        } else {
                super() で親クラスのコンストラクタを無引数で呼び出す
        }

        そのコンストラクタのsuper(),this() 以降の式文を実行していく
}       

ということは、コンストラクタには大きな制限がある。しかし、これはコンストラクタの部分によって違うので、まずコンストラクタ例を見て頂きたい。

public class SomeClass extends BaseClass {
   int var;
   public SomeClass( ) {  /* コンストラクタ */
       this( "default value", 0 );  /* 2引数コンストラクタを呼び出す */
   }

   public SomeClass( String s, int v ) {
       super( s );  /* 親クラスコンストラクタを呼び出す。この時点で引数は利用可*/
       /* 以降はインスタンス変数やインスタンスメソッドも利用可 */
       var = v;
   }

   /* 引数の形態に合わせた静的多相のコンビニエンス・コンストラクタ */
   public SomeClass( int v ) { this( "default value", v ); }
   public SomeClass( String s ) { this( x, 0 ); }
}

つまり、コンストラクタでは、その最初の文として親コンストラクタを呼び出す super(), あるいは自身の別な引数を取るコンストラクタを呼び出す this() が記述できる。ただし、これは省略もできるが、super(); this(); のように両方とも1つのコンストラクタから呼び出すことはできない(恣意的な制限だが...)。

そして、この super(), this() の呼び出しの時点では、まだインスタンスは出来上がっていない。だから、インスタンス自身を示す this, インスタンス自身の親から継承した部分を示す super の2つの特殊ポインタは利用できないのである。これを表にまとめよう。

 super,this行の引数それ以降
コンストラクタ引数使える使える
そのクラスのクラスメソッド・クラス変数使える使える
そのクラスのインスタンスメソッド・インスタンス変数使えない使える
無関係なクラスのメソッド・変数使える使える

コンストラクタの制限

その他に上記ロジックに従った、制限事項もいろいろとある。これらを列挙しよう。

  1. コンストラクタは、同一クラスのコンストラクタから this(引数) によって、あるいは、直接のサブクラスから super(引数) のかたちによって以外に、new 演算子を介さずに明示的にメソッドのように呼び出されることはできない。

  2. コンストラクタが存在しないときには、あたかも public theClass() { super(); } が定義されているかのようにコンパイラはデフォルトコンストラクタを生成する。

  3. 親クラスのコンストラクタを呼び出す super(), 同一クラスのコンストラクタを呼び出す this() を書けるが、コンストラクタの先頭になければならない。

  4. 引数のあるコンストラクタを定義できるが、継承がなされて、派生クラスで親クラスの引数のあるコンストラクタを呼び出す場合には、明示的に super(引数リスト) で呼び出す必要がある。これは「コンストラクタは真に継承されない」ことを示している。

  5. この引数コンストラクタの継承のときに、当然引数のあるコンストラクタを定義するが、引数のあるコンストラクタを定義してしまうと、コンパイラはデフォルトコンストラクタの生成を抑止してしまう。つまり、引数のあるコンストラクタを定義した場合には、引数のないコンストラクタも適当な初期値で初期化するようなものを定義する必要がある。無引数コンストラクタの存在を前提とするインスタンス生成方法が別にあるので、その対応が必要である場合が多い。

  6. 親コンストラクタ引数super(), 同一クラスコンストラクタ this() にはインスタンス変数を渡すことはできない。なぜなら、まだ完全に子クラスは生成されていないからである。しかし、コンストラクタに渡って来たコンストラクタ引数は渡せるので、一般にこれを親コンストラクタに更に渡すことも多い。

  7. 同様に、親コンストラクタの引数にそのクラスのインスタンスメソッド呼び出し式を書くこともできない。つまり、親クラスコンストラクタ呼び出しの時点では、まだ this ポインタは有効ではないのである。逆に言えば、クラス変数の参照やクラスメソッド呼び出しは使える。

  8. コンストラクタ内で例外を投げることは可能だが、この場合 catch 節に即時に制御が移り、オブジェクトは生成されない。

  9. 動的ローディング機能や Java Bean などでは、引数のないデフォルトコンストラクタの存在を前提にする場合がある。

finalize メソッド

また、デストラクタに相当する、コンストラクタの反対の役割をする finalize() メソッドが存在する(これは Object クラスのメソッドである)。とはいえ、Java ではガベージコレクタがインスタンスの管理をしているので、finalize() を明示的に呼び出すことはしない。特別な終了処理が必要なインスタンス変数がある場合、単なるそのインスタンス変数の破棄だけでは済まない処理がある場合にのみ、finalize メソッドを上書きすべきである。



copyright by K.Sugiura, 1996-2006