|
|
コンストラクタ |
H.Kamifuji . |
ここではコンストラクタの利用方法について解説いたします。 当ページでは、Linux CentOS7 の Gnome で動作テストしています。 現在(2021/08)では、JDK-16.0.2 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2021/11)では、JDK-17.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2023/04)では、JDK-20.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2024/10)では、JDK-23 にアップされています。一部、上位互換について、見直しを行っていきます。 |
|
ここではコンストラクタについて見ていきます。コンストラクタとは、クラスからオブジェクトを作成した際に、自動的に実行されるメソッドのことで、メンバ変数の初期化などの主に行います。 まず下記のサンプルを見て下さい。 サンプルプログラム下記のサンプルを実行してみよう。class ctest7{ public static void main(String args[]){ Television tv1 = new Television(); tv1.dispChannel(); } } class Television{ private int channelNo; private String housouKyoku; public void setChannel(int newChannelNo){ channelNo = newChannelNo; if (channelNo == 1){ housouKyoku = "FujiTV"; }else if (channelNo == 3){ housouKyoku = "NHK"; } } public void dispChannel(){ System.out.println("現在のチャンネルは" + housouKyoku + "です"); } }一見何の問題も無いように見えますが、実際に実行してみると下記のようになります。 [xxxxxxxx@dddddddddd Constructor]$ java ctest7 現在のチャンネルはnullです [xxxxxxxx@dddddddddd Constructor]$チャンネルは「null」と表示されてしまいました。これはメンバ変数の初期化を行っておらず、また一度も値を格納しないままメンバ変数の値を表示しようとしたためです。 このようにクラスからオブジェクトを作成した際に、まずメンバ変数の初期化を行っておかなければいけない場合が多々あります。メンバ変数の定義時に合わせて初期値を代入しておくことでも解決できますが、その場合は固定した値しか設定することが出来ません。 こういった場合にコンストラクタを使います。コンストラクタとは、クラスからオブジェクトが作成された際に、無条件で実行されるメソッドのことで下記のような記述を行います。 クラス名(){ .... }通常のメソッドの戻り値の型指定が無いものと考えて下さい。またメソッド名はクラス名と同じ名前にします。 では実際の例で試してみましょう。 サンプルプログラム下記のサンプルを実行してみよう。class ctest8{ public static void main(String args[]){ Television tv1 = new Television(); tv1.dispChannel(); } } class Television{ private int channelNo; private String housouKyoku; Television(){ channelNo = 1; housouKyoku = "FujiTV"; } public void setChannel(int newChannelNo){ channelNo = newChannelNo; if (channelNo == 1){ housouKyoku = "FujiTV"; }else if (channelNo == 3){ housouKyoku = "NHK"; } } public void dispChannel(){ System.out.println("現在のチャンネルは" + housouKyoku + "です"); } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Constructor]$ java ctest8 現在のチャンネルはFujiTVです [xxxxxxxx@dddddddddd Constructor]$今回はメンバ変数に値を格納するメソッドを呼び出してはいないのですがコンストラクタによってオブジェクト作成時にメンバ変数に初期値が格納されていますので、いきなり現在の放送局について出力しても適切な値が設定されています。 |
コンストラクタにはメソッドと同じように引数を指定することが出来ます。引数がある場合のコンストラクタを使用する場合、そのクラスからオブジェクトを作成する際に下記のように記述します。 クラス名 オブジェクト名 = new クラス名(引数);実際の使い方としては下記のようになります。 サンプルプログラム下記のサンプルを実行してみよう。class ctest9{ public static void main(String args[]){ Television tv1 = new Television("居間"); tv1.dispChannel(); Television tv2 = new Television("台所"); tv2.dispChannel(); } } class Television{ private int channelNo; private String place; Television(String newPlace){ channelNo = 1; place = newPlace; } public void setChannel(int newChannelNo){ channelNo = newChannelNo; } public void dispChannel(){ System.out.println("現在のチャンネルは" + channelNo+ "です"); System.out.println("設置してある場所は" + place + "です"); } }上記ではデフォルトの値を設定したいチャンネルはコンストラクタの中で固定の値を設定し、オブジェクト毎に異なる値をオブジェクト作成時に設定したい設置場所はコンストラクタの引数に指定してコンストラクタの中で設定しています。 上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Constructor]$ java ctest9 現在のチャンネルは1です 設置してある場所は居間です 現在のチャンネルは1です 設置してある場所は台所です [xxxxxxxx@dddddddddd Constructor]$ |
メソッドの場合と同様に、引数の数や型が異なるものであれば、1つのクラスの中に複数のコンストラクタを記述できます。 では実際に試してみます。 サンプルプログラム下記のサンプルを実行してみよう。class ctest10{ public static void main(String args[]){ Television tv1 = new Television("居間"); tv1.dispChannel(); Television tv2 = new Television(); tv2.dispChannel(); } } class Television{ private int channelNo; private String place; Television(){ channelNo = 1; place = "未定"; } Television(String newPlace){ channelNo = 1; place = newPlace; } public void setChannel(int newChannelNo){ channelNo = newChannelNo; } public void dispChannel(){ System.out.println("現在のチャンネルは" + channelNo+ "です"); System.out.println("設置してある場所は" + place + "です"); } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Constructor]$ java ctest10 現在のチャンネルは1です 設置してある場所は居間です 現在のチャンネルは1です 設置してある場所は未定です [xxxxxxxx@dddddddddd Constructor]$ コンストラクタからコンストラクタを呼び出すまたコンストラクタから他のコンストラクタを呼び出すことも出来ます。その場合の記述方法は下記のようになります。クラス名(){ this(引数); } クラス名(引数){ .... }コンストラクタの中から他のコンストラクタを呼び出す場合には、thisを使って「this(引数)」の形で呼び出します。ここで引数とは呼び出したいコンストラクタで指定されている引数を記述します。 先ほどのサンプルを書き直すと下記のようになります。 class Television{ private int channelNo; private String place; Television(){ this("未定"); } Television(String newPlace){ channelNo = 1; place = newPlace; } public void setChannel(int newChannelNo){ channelNo = newChannelNo; } public void dispChannel(){ System.out.println("現在のチャンネルは" + channelNo+ "です"); System.out.println("設置してある場所は" + place + "です"); } } |
コンストラクタを1つも定義していない場合、自動的にデフォルトコンストラクタと呼ばれるコンストラクタが作成されます。 クラス名() { }見て頂くと分かりますが、引数無しで中身が空のコンストラクタが作成されます。中身が空ですので何もしません。 では、何故このような空のコンストラクタが意味を持つのかと言いますと、コンストラクタは実行される前にまず自分自身の親クラスのコンストラクタを呼び出すようになっています。詳しくはクラスの継承の箇所でご説明しますが、コンストラクタの1行目には「super()」という親クラスのコンストラクタを呼び出す動作を行います。これはデフォルトのコンストラクタでも同じですので実際は下記のようになっています。 クラス名() { super(); }何もしないようなデフォルトのコンストラクタですが、親クラスのコンストラクタを呼び出すという動作をしているため意味はあるわけです。 デフォルトコンストラクタの注意点明示的にコンストラクタを1つでも記述した場合、デフォルトコンストラクタは作成されません。引数無しのコンストラクタを記述したのであれば問題無いのですが、引数無しのコンストラクタを定義せず、引数があるコンストラクタだけ記述した場合にもデフォルトのコンストラクタは作成されないため、引数無しでそのクラスのオブジェクトを作成する事が出来なくなりますので注意して下さい。 サンプルで試してみましょう。例えば下記のようなプログラムであれば問題はありません。 class ctest11_1{ public static void main(String args[]){ Test test = new Test(); } } class Test{ }まさしく何もしないプログラムですが一応コンパイルは通りますし実行も出来ます。このプログラムがコンパイルエラーとならないのは、デフォルトのコンストラクタが自動的に作成されているためです。つまり下記のような感じです。 class ctest11_1{ public static void main(String args[]){ Test test = new Test(); } } class Test{ Test(){ } }Testクラスのオブジェクトを作成する時、引数無しで作成していますが、引数無しのコンストラクタは自動で作成されるため問題はありませんでした。 次に引数があるようなコンストラクタを1つだけ定義した場合です。これは例えば引数無しと引数有りの場合があり、引数有りの場合だけ何か処理をしたいと考えた場合などです。 class ctest11_1{ public static void main(String args[]){ Test test = new Test(); } } class Test{ Test(int num){ } }これはコンパイルエラーとなります。 [xxxxxxxx@dddddddddd Constructor]$ javac ctest11_1.java ctest11_1.java:3: エラー: クラス Testのコンストラクタ Testは指定された型に適用できません。 Test test = new Test(); ^ 期待値: int 検出値: 引数がありません 理由: 実引数リストと仮引数リストの長さが異なります エラー1個 [xxxxxxxx@dddddddddd Constructor]$Testクラスのオブジェクトを引数無しで作成しようとしていますが、引数無しのコンストラクタが存在しないためエラーとなるわけです。コンストラクタが1つも無い場合にはデフォルトのコンストラクタが自動で作成されたのでよかったのですが、1つでもコンストラクタを自分で定義するとデフォルトコンストラクタは作成されないためエラーとなってしまいます。 このようなことにならないためにも、引数有りのコンストラクタを定義する場合には、引数無しのコンストラクタも(中身が例え空だったとしても)記述しておくようにして下さい。 class ctest11_3{ public static void main(String args[]){ Test test = new Test(); } } class Test{ Test(){ } Test(int num){ } }上記であればコンパイルエラーとはなりませんし実行も出来ます。 |
|