Search on the blog

2013年9月15日日曜日

Javaのコンストラクタについて

 Javaのコンストラクタについて理解が曖昧だったのでまとめてみました。

デフォルトコンストラクタについて
Blochの『Effective Java』に以下の記述があります[1]。

In the absence of explicit constructors, however, the compiler provides a public parameterless default constructor.
(和訳: 明示的にコンストラクタを定義しなければ、パラメータ無しのpublicなデフォルトコンストラクタがコンパイラによって作られる。)

以下の場合は明示的に定義はしていませんが、Clazz()が呼ばれることになります。
package com.kenjih.test;

public class Clazz {
    public static void main(String[] args) {
        new Clazz();
    }
}

ここで昔ちょっとはまったのがこれです。上のソースを拡張してパラメータ付きのコンストラクタを追追加してみます。
package com.kenjih.test;

public class Clazz {
    public Clazz(Object param) {
        // do something
    }
    
    public static void main(String[] args) {
        new Clazz("hoge");
        new Clazz();
    }
}
これだとnew Clazz(); のところでコンパイルエラーになってしまいます。明示的にコンストラクタを定義した結果、コンパイラがデフォルトコンストラクタを生成しなくなったためです。以下のように書くとコンパイルエラーは無くなります。
package com.kenjih.test;

public class Clazz {
    public Clazz() {}
 
    public Clazz(Object param) {
        // do something
    }
    
    public static void main(String[] args) {
        new Clazz("hoge");
        new Clazz();
    }
}
「デフォルトコンストラクタが提供されるのは、コンストラクタを1つも定義しなかった場合のみ」ということに注意が必要です。


拡張クラスのコンストラクタについて
同じくBlochの『Effective Java』に以下の記述があります[1]。

All constructors must invoke a superclass constructor, explicitly or implicitly.
(和訳: すべてのコンストラクタはスーパークラスのコンストラクタを、明示的にもしくは暗黙のうちに、呼び出さなければならない。)
明示的に/暗黙のうちにの条件がよく分からなかったので簡単なサンプルソースを書いて調べてみました。
package com.kenjih.test;

public class Par {
    public Par() {
        System.out.println("Par() was invoked.");        
    }
    
    public static void main(String[] args) {
        new Chi();
    }
}

class Chi extends Par {
    public Chi() {
        System.out.println("Chi() was invoked.");
    }
}
実行結果は以下のとおりです。
$ Par() was invoked.
$ Chi() was invoked.
Chiクラスのコンストラクタを呼んだ際に、親クラスであるParのコンストラクタが暗黙のうちに呼ばれています。
親クラスに複数のコンストラクタが存在した場合はどうなるでしょうか?
package com.kenjih.test;

public class Par {
    public Par() {
        System.out.println("Par() was invoked.");        
    }
    
    public Par(Object param) {
        System.out.println("Par(Object) was invoked.");       
    }
    
    public static void main(String[] args) {
        new Chi();
    }
}

class Chi extends Par {
    public Chi() {
        System.out.println("Chi() was invoked.");
    }
}
結果は先ほどと同様です。
$ Par() was invoked.
$ Chi() was invoked.
ということは、「親クラスのコンストラクタを明示的に呼び出さなかった場合は、親クラスのパラメータ無しコンストラクタが暗黙のうちに呼ばれる。」と考えればよさそうです。一応調べてみましたが上記の理解で正しそうです[2]。

If you are satisfied with the default constructor in the superclass, there is no need to make a call to it because it will be supplied automatically.

ということは、以下のようなコードを書くとコンパイルエラーになるということですかね?
package com.kenjih.test;

public class Par {
    public Par(Object param) {
        System.out.println("Par(Object) was invoked.");       
    }
    
    public static void main(String[] args) {
        new Chi();
    }
}

class Chi extends Par {
    public Chi() {
        System.out.println("Chi() was invoked.");
    }
}
予想どおりエラー(Implicit super constructor Par() is undefined. Must explicitly invoke another constructor)になりました。

参考
[1] Effective Java

[2] http://www.leepoint.net/notes-java/oop/constructors/constructor.html

0 件のコメント:

コメントを投稿