Javaのコンストラクタ

Javaのコンストラクタはクラスのインスタンスを作成するために使用されます。コンストラクタは、メソッドとほぼ同じですが、2つの違いがあります。それはクラス名と同じ名前であり、戻り値の型がありません。時にはコンストラクタはオブジェクトを初期化するための特別なメソッドとも呼ばれます。

Javaのコンストラクタ

クラスのインスタンスを作成するためにnewキーワードを使用すると、コンストラクタが呼び出され、クラスのオブジェクトが返されます。コンストラクタはクラスにのみオブジェクトを返すことができるため、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つの種類のコンストラクタがあります。

  1. デフォルトコンストラクタ
  2. 引数なしのコンストラクタ
  3. パラメータ化されたコンストラクタ

これらのすべてのコンストラクタの種類と例題について見ていきましょう。

Javaにおけるデフォルトコンストラクタ

クラスのコードに常にコンストラクタの実装を提供する必要はありません。コンストラクタを提供しない場合、Javaはデフォルトのコンストラクタの実装を提供してくれます。コンストラクタを明示的に定義しないため、デフォルトコンストラクタが使用される単純なプログラムを見てみましょう。

package com.journaldev.constructor;

public class Data {

	public static void main(String[] args) {
		Data d = new Data();
	}
}
  1. デフォルトコンストラクタの役割はオブジェクトの初期化と呼び出し元に返すことだけです。
  2. デフォルトコンストラクタは常に引数なしであり、既存のコンストラクタが定義されていない場合にのみJavaコンパイラによって提供されます。
  3. ほとんどの場合、デフォルトコンストラクタ自体で十分です。他のプロパティは、getterおよびsetterメソッドを介してアクセスおよび初期化できます。

引数なしのコンストラクタ

引数なしのコンストラクタは、ノーアーグスコンストラクタと呼ばれます。これはデフォルトコンストラクタをオーバーライドするようなもので、リソースのチェック、ネットワーク接続、ログ出力などの事前初期化処理を行うために使用されます。Javaにおけるノーアーグスコンストラクタについて簡単に見てみましょう。

package com.journaldev.constructor;

public class Data {
        //ノーアーグスコンストラクタ
	public Data() {
		System.out.println("No-Args Constructor");
	}
	public static void main(String[] args) {
		Data d = new Data();
	}
}

次に、new Data()を呼び出すと、ノーアーグスコンストラクタが呼び出されます。以下のイメージは、プログラムのコンソール出力を確認するためのものです。

パラメータ付きコンストラクタ

引数を持つコンストラクタは、パラメータ付きコンストラクタと呼ばれます。Javaにおけるパラメータ付きコンストラクタの例を見てみましょう。

package com.journaldev.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.journaldev.constructor;

public class Data {

	private String name;
	private int id;

	//ノーアーグスコンストラクタ
	public Data() {
		this.name = "Default Name";
	}
	//1つのパラメータを持つコンストラクタ
	public Data(String n) {
		this.name = n;
	}
	//2つのパラメータを持つコンストラクタ
	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におけるプライベートコンストラクタ

注意点として、コンストラクタにはabstractfinalstaticsynchronizedのキーワードは使用できません。ただし、アクセス修飾子を使用してクラスオブジェクトのインスタンス化を制御することは可能です。publicおよびdefaultアクセスは問題ありませんが、コンストラクタをプライベートにする目的は何でしょうか?その場合、他のクラスはそのクラスのインスタンスを作成することができません。では、コンストラクタをプライベートにする目的はシングルトンデザインパターンの実装です。Javaはデフォルトコンストラクタを自動的に提供するため、明示的にコンストラクタを作成してプライベートにする必要があります。クライアントクラスは、クラスのインスタンスを取得するためのユーティリティの静的メソッドを提供されます。以下はDataクラスのプライベートコンストラクタの例です。

// プライベートコンストラクタ
private Data() {
	// シングルトンパターンの実装のための空のコンストラクタ
	// クラスのgetInstance()メソッド内で使用されるコードを持つことができます
}

Javaにおけるコンストラクタのチェーン

同じクラスのコンストラクタが別のコンストラクタを呼び出す場合、それはコンストラクタのチェーンと呼ばれます。クラスの別のコンストラクタを呼び出すために、thisキーワードを使用する必要があります。時には、クラス変数のデフォルト値を設定するために使用されます。別のコンストラクタの呼び出しは、コードブロック内の最初のステートメントである必要があります。また、無限ループを作成する再帰的な呼び出しが存在しないように注意してください。以下はJavaプログラムでのコンストラクタのチェーンの例です。

package com.journaldev.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;
	}

}

I have overridden the toString() method to print some useful information about Employee object. Below is the output produced by above program.

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.journaldev.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.journaldev.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クラスの引数なしコンストラクタが呼び出されます。したがって、出力はそのようになります。次に、Student st = new Student(34, "Pankaj");のようにStudentクラスのパラメータ付きコンストラクタを使用した場合、出力は次のようになります。

Person Created with Age = 34
Student Created with name = Pankaj

ここでは、スーパークラスのコンストラクタを明示的に呼び出しているため、Javaは追加の作業を行う必要はありません。

Javaのコピーコンストラクタ

Javaのコピーコンストラクタは、同じクラスのオブジェクトを引数として受け取り、それをコピーします。処理を行うために他のオブジェクトのコピーが必要な場合もあります。以下の方法で実現することができます:

  1. クローニングを実装する
  2. オブジェクトのディープコピーを提供するためのユーティリティメソッドを提供する
  3. コピーコンストラクタを持つ

では、どのようにコピーコンストラクタを書くか見てみましょう。以下のようなFruitsクラスがあるとします。

package com.journaldev.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(Fruits fr)は、ディープコピーを行ってオブジェクトのコピーを返します。コピーオブジェクトをコピーするためにコピーコンストラクタを持つことがなぜ良いかを理解するために、テストプログラムを見てみましょう。

package com.journaldev.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のコンストラクタについては以上です。

Source:
https://www.digitalocean.com/community/tutorials/constructor-in-java