Javaにおけるコンストラクタ
Javaでのコンストラクタは、クラスのインスタンスを作成するために使用されます。コンストラクタは、メソッドとほぼ同じですが、2つの点で異なります。それはクラス名と同じ名前であり、戻り値の型を持ちません。時にはコンストラクタは、オブジェクトを初期化するための特別なメソッドとしても言及されます。
Javaにおけるコンストラクタ
クラスのインスタンスを作成するために新しいキーワードを使用すると、コンストラクタが呼び出され、クラスのオブジェクトが返されます。コンストラクタはクラスにのみオブジェクトを返すことができるため、それはJavaランタイムによって暗黙的に行われ、それに戻り値の型を追加することはありません。もしコンストラクタに戻り値の型を追加すると、それはクラスのメソッドになります。これがJavaランタイムが通常のメソッドとコンストラクタを区別する方法です。以下のようなEmployeeクラスのコードがあると仮定しましょう。
public Employee() {
System.out.println("Employee Constructor");
}
public Employee Employee() {
System.out.println("Employee Method");
return new Employee();
}
最初のものはコンストラクタで、返り値の型も返り値の文もありませんので、注意してください。2番目のものは通常のメソッドで、再び最初のコンストラクタを呼び出して、その後Employeeのインスタンスを返します。クラス名と同じメソッド名にしない方が混乱を招かないので、推奨されています。
Javaにおけるコンストラクタの種類
Javaには3種類のコンストラクタがあります。
-
- デフォルトコンストラクター
-
- 引数なしコンストラクター
- パラメーター付きコンストラクター
これらのコンストラクタの種類と例題プログラムを見てみましょう。
Javaにおけるデフォルトコンストラクタ
クラスコードで常にコンストラクタ実装を提供する必要はありません。コンストラクタを提供しない場合、Javaはデフォルトのコンストラクタ実装を提供してくれます。コンストラクタを明示的に定義しないため、デフォルトのコンストラクタが使用される単純なプログラムを見てみましょう。
package com.scdev.constructor;
public class Data {
public static void main(String[] args) {
Data d = new Data();
}
}
-
- デフォルトコンストラクタの役割は、オブジェクトを初期化して呼び出し元のコードに返すことです。
-
- デフォルトコンストラクタは常に引数なしであり、既存のコンストラクタが定義されていない場合にのみ、Javaコンパイラによって提供されます。
- ほとんどの場合、他のプロパティは、ゲッターセッターメソッドを通じてアクセスおよび初期化するため、デフォルトコンストラクタ自体で十分です。
引数のないコンストラクタ
引数なしのコンストラクタは、no-argsコンストラクタと呼ばれます。これはデフォルトのコンストラクタを上書きするようなものであり、リソースの確認、ネットワーク接続の確認、ログの記録など、事前の初期化処理を行うために使用されます。では、Javaにおけるno-argsコンストラクタを簡単に見てみましょう。
package com.scdev.constructor;
public class Data {
//no-args constructor
public Data() {
System.out.println("No-Args Constructor");
}
public static void main(String[] args) {
Data d = new Data();
}
}
パラメータ化されたコンストラクタ
引数を持つコンストラクターはパラメーター化されたコンストラクターと呼ばれます。Javaにおけるパラメーター化されたコンストラクターの例を見てみましょう。
package com.scdev.constructor;
public class Data {
private String name;
public Data(String n) {
System.out.println("Parameterized Constructor");
this.name = n;
}
public String getName() {
return name;
}
public static void main(String[] args) {
Data d = new Data("Java");
System.out.println(d.getName());
}
}
Javaにおけるコンストラクタのオーバーロード
複数のコンストラクタがある場合、それはJavaにおけるコンストラクタのオーバーロードです。Javaのプログラムにおけるコンストラクタのオーバーロードの例を見てみましょう。
package com.scdev.constructor;
public class Data {
private String name;
private int id;
//no-args constructor
public Data() {
this.name = "Default Name";
}
//one parameter constructor
public Data(String n) {
this.name = n;
}
//two parameter constructor
public Data(String n, int i) {
this.name = n;
this.id = i;
}
public String getName() {
return name;
}
public int getId() {
return id;
}
@Override
public String toString() {
return "ID="+id+", Name="+name;
}
public static void main(String[] args) {
Data d = new Data();
System.out.println(d);
d = new Data("Java");
System.out.println(d);
d = new Data("Pankaj", 25);
System.out.println(d);
}
}
Javaにおけるプライベートコンストラクタ
注意してください、コンストラクターでは抽象、最終、静的、および同期キーワードは使用できません。ただし、アクセス修飾子を使用してクラスオブジェクトのインスタンス化を制御することはできます。publicとデフォルトのアクセスを使用することも問題ありませんが、コンストラクターをプライベートにする利点は何でしょうか?その場合、他のクラスはそのクラスのインスタンスを作成することはできません。では、コンストラクターをプライベートにするのは、シングルトンデザインパターンを実装したい場合です。Javaはデフォルトコンストラクターを自動的に提供するため、明示的にコンストラクターを作成し、プライベートに保持する必要があります。クライアントクラスは、クラスのインスタンスを取得するためのユーティリティの静的メソッドが提供されます。Dataクラスのプライベートコンストラクターの例を以下に示します。
// private constructor
private Data() {
//empty constructor for singleton pattern implementation
//can have code to be used inside the getInstance() method of class
}
Javaにおけるコンストラクターチェイニング
同じクラスのコンストラクタが別のコンストラクタを呼び出す場合、それはコンストラクタチェインと呼ばれます。クラスの別のコンストラクタを呼び出すためには、thisキーワードを使用する必要があります。時にはクラス変数のデフォルト値を設定するために使用されます。なお、別のコンストラクタ呼び出しはコードブロック内の最初の文でなければなりません。また、無限ループを作成する再帰呼び出しは行ってはなりません。Javaプログラムのコンストラクタチェインの例を見てみましょう。
package com.scdev.constructor;
public class Employee {
private int id;
private String name;
public Employee() {
this("John Doe", 999);
System.out.println("Default Employee Created");
}
public Employee(int i) {
this("John Doe", i);
System.out.println("Employee Created with Default Name");
}
public Employee(String s, int i) {
this.id = i;
this.name = s;
System.out.println("Employee Created");
}
public static void main(String[] args) {
Employee emp = new Employee();
System.out.println(emp);
Employee emp1 = new Employee(10);
System.out.println(emp1);
Employee emp2 = new Employee("Pankaj", 20);
System.out.println(emp2);
}
@Override
public String toString() {
return "ID = "+id+", Name = "+name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
私はtoString()メソッドをオーバーライドして、Employeeオブジェクトに関する有用な情報を表示しています。以下は上記プログラムによって生成された出力です。
Employee Created
Default Employee Created
ID = 999, Name = John Doe
Employee Created
Employee Created with Default Name
ID = 10, Name = John Doe
Employee Created
ID = 20, Name = Pankaj
別のコンストラクターから呼び出されていることに注意してください。これがコンストラクターチェーンのプロセスと呼ばれています。
Javaのスーパーコンストラクタ
時には、クラスはスーパークラスから継承されることがあります。その場合、スーパークラスのコンストラクタを呼び出さなければならない場合、superキーワードを使用することができます。スーパークラスのコンストラクタを呼び出す場合、子クラスのコンストラクタ内で最初の文として呼び出す必要があります。また、子クラスのコンストラクタをインスタンス化する際には、Javaはまずスーパークラスを初期化し、次に子クラスを初期化します。したがって、スーパークラスのコンストラクタが明示的に呼び出されない場合、Javaランタイムによってデフォルトまたは引数なしのコンストラクタが呼び出されます。これらの概念をいくつかの例題を通して理解しましょう。以下のような2つのクラスがあると仮定してください。
package com.scdev.constructor;
public class Person {
private int age;
public Person() {
System.out.println("Person Created");
}
public Person(int i) {
this.age = i;
System.out.println("Person Created with Age = " + i);
}
}
package com.scdev.constructor;
public class Student extends Person {
private String name;
public Student() {
System.out.println("Student Created");
}
public Student(int i, String n) {
super(i); // super class constructor called
this.name = n;
System.out.println("Student Created with name = " + n);
}
}
以下のようにStudentオブジェクトを作成すると、
Student st = new Student();
上記のコードの出力は何になりますか?上記のコードの出力は以下の通りです:
Person Created
Student Created
最初の文にスーパーコールがなかったため、呼び出しはStudentクラスの引数なしコンストラクタに移ります。そのため、引数なしまたはデフォルトのPersonクラスのコンストラクタが呼び出されます。よって、出力は次のようになります。もしStudentクラスのパラメータ付きコンストラクタを使用している場合、Student st = new Student(34, “Pankaj”);、出力は次のようになります。
Person Created with Age = 34
Student Created with name = Pankaj
ここでは、明示的にスーパークラスのコンストラクタを呼び出しているため、Javaは自分たちの側から余分な作業をする必要はありませんので、出力は明確です。
Javaのコピー・コンストラクター
Javaのコピーコンストラクタは、同じクラスのオブジェクトを引数として受け取り、それのコピーを作成します。時には他のオブジェクトのコピーが必要で、処理を行うために以下の方法で行うことができます。
-
- クローニングを実装し、オブジェクトのディープコピーのためのユーティリティメソッドを提供する。
- コピー用のコンストラクタを持つ。
では、コピー(複製)コンストラクタをどのように書くか見てみましょう。以下のようなFruitsというクラスがあると仮定してください。
package com.scdev.constructor;
import java.util.ArrayList;
import java.util.List;
public class Fruits {
private List<String> fruitsList;
public List<String> getFruitsList() {
return fruitsList;
}
public void setFruitsList(List<String> fruitsList) {
this.fruitsList = fruitsList;
}
public Fruits(List<String> fl) {
this.fruitsList = fl;
}
public Fruits(Fruits fr) {
List<String> fl = new ArrayList<>();
for (String f : fr.getFruitsList()) {
fl.add(f);
}
this.fruitsList = fl;
}
}
Fruitsクラスがオブジェクトのコピーを返すためにディープコピーを実行していることに注意してください。コピーを作成するためにコピーコンストラクタを使用する方が良い理由を理解するために、テストプログラムを見てみましょう。
package com.scdev.constructor;
import java.util.ArrayList;
import java.util.List;
public class CopyConstructorTest {
public static void main(String[] args) {
List<String> fl = new ArrayList<>();
fl.add("Mango");
fl.add("Orange");
Fruits fr = new Fruits(fl);
System.out.println(fr.getFruitsList());
Fruits frCopy = fr;
frCopy.getFruitsList().add("Apple");
System.out.println(fr.getFruitsList());
frCopy = new Fruits(fr);
frCopy.getFruitsList().add("Banana");
System.out.println(fr.getFruitsList());
System.out.println(frCopy.getFruitsList());
}
}
上記のプログラムの出力は次の通りです。
[Mango, Orange]
[Mango, Orange, Apple]
[Mango, Orange, Apple]
[Mango, Orange, Apple, Banana]
Javaでコピーのコンストラクタを使用する場合、元のオブジェクトとそのコピーは互いに関係がなく、どちらかの変更が他方に反映されないことに注意してください。これがJavaにおけるコンストラクタのすべてです。