1. 概要

Scalaのコンストラクターは、オブジェクトを初期化するために使用される特別なメソッドを記述します。 そのクラスのオブジェクトを作成する必要がある場合は、クラスのコンストラクターを呼び出します。 オブジェクト属性の初期値またはデフォルト値を設定するために使用できます。

2. コンストラクター

コンストラクターは、クラスの基本的な青写真を定義し、オブジェクト作成のためにいくつかの値の存在を強制する場合があります。

class Employee(name : String, email : String)

クラスEmployeeと、String型の名前とString型の電子メールの2つの引数を取るコンストラクターを定義しました。 コンストラクターを作成することにより、すべての従業員が名前と電子メールを持っている必要があることを強制しました。 従業員のインスタンスを作成する場合は、これらの値を指定する必要があります。

ここで、クラスを拡張して、それに関連付けられた役割を持たせたとします。 役割は、ワーカーの場合は0、マネージャーの場合は1などになります。 コンストラクターに別の引数を簡単に追加できます。

 class Employee(name : String, email : String, role : Int)

しかし、誰かに任意の役割を持つ従業員を作成したり、役割に無効な値を提供したりする機能を持たせたくない場合はどうでしょうか。 このコンストラクターを使用する人は誰でも、任意の従業員に任意の役割を割り当てることができますが、それは望ましくありません。

代わりに、役割を提供せずに従業員を作成する機能を公開したいと考えています。 これは、privateおよびprotectedコンストラクターを使用して実行できます。

3. プライベートコンストラクター

new キーワードを直接呼び出してクラスのインスタンス化を防ぎたい場合は、プライベートコンストラクターを使用しますが、代わりに、クラスをインスタンス化する他の手段を提供します。

一部のコンストラクターは、クラスの機能にとって重要な非常に複雑な構造を持っています。 これらのコンストラクターの設計者は、これらのコンストラクターをプライベートにし、代わりにこれらのクラスのインスタンスを作成するための読みやすいメソッドを提供することを決定することがよくあります。

プライベートコンストラクターでクラスを拡張することはできません。

完璧な例は、ScalaのVectorクラスです。 コンストラクターはプライベートであるため、newキーワードを使用してクラスをインスタンス化することはできません。 代わりに、 Vector#applyメソッドを使用します。

前の例に戻ると、クラス名の後に private キーワードを追加することで、Employeeクラスをプライベートにすることができます。

class Employee private (name : String, email : String, role : Int)

このように、新しいキーワードで Employee をインスタンス化しようとすると、ScalaREPLからエラーが発生します。

scala> val employee = new Employee("John Doe", "[email protected]")
<console>:12: error: constructor Employee in class Employee cannot be accessed in object $iw
       val employee = new Employee("John Doe", "[email protected]")

これにより、コンストラクターがプライベートになり、newキーワードがEmployeeクラスのインスタンスの作成に使用されないようになりました。

3.1. インスタンスの作成

コンストラクターをプライベートにしたので、最初に頭に浮かぶ質問は、クラスをインスタンス化する方法です。 これを行う1つの方法は、シングルトンパターンに従ってクラスのインスタンスを作成するために使用できるコンパニオンオブジェクトを定義することです。

コンパニオンオブジェクトには、対応するクラスのプライベートコンストラクターにアクセスする機能があります。 

Employeeクラスのコンパニオンオブジェクトを簡単に定義できます。

class Employee private(name: String, email: String, role: Int)
object Employee {
  def createWorker(name: String, email: String): Employee = new Employee(name, email, 0)
  def createManager(name: String, email: String): Employee = new Employee(name, email, 1)
}
val worker = Employee.createWorker("John Doe", "[email protected]")
val manager = Employee.createManager("John Smith", "[email protected]")

このように、 new キーワードを使用する代わりに、 Employee#createWorkerまたはEmployee#createManagerを簡単に呼び出すことができます。

4. 保護されたコンストラクター

前の例で、プライベートコンストラクターを持つクラスは拡張できないことを説明しました。 しかし、コンストラクターのプライバシーを維持し、それでもそのクラスを拡張できるようにしたい場合はどうでしょうか。

これは、protectedコンストラクターを使用して実行できます。

保護されたコンストラクターは、新しいキーワードを使用してクラスをインスタンス化できないという点でプライベートコンストラクターに似ていますが、拡張することはできます

この例では、コンパニオンオブジェクトでメソッドを使用するのではなく、Employeeクラスを拡張するWorkerクラスとManagerクラスを定義できます。

class Employee protected(name: String, email: String, role: Int)
class Worker(name: String, email: String) extends Employee(name, email, 0)
class Manager(name: String, email: String) extends Employee(name, email, 1)

val worker = new Worker("John Doe","[email protected]")
val manager = new Manager("John Smith","[email protected]")

protected コンストラクターを使用して、コンストラクターのプライバシーを維持しましたが、クラスを拡張できるようにしました。 new キーワードを使用して、WorkerクラスとManagerクラスのインスタンスを簡単に作成できますが、Employeeクラスは作成できません。

5. 結論

この記事では、Scalaでコンストラクターを定義し、これらのコンストラクターへのアクセスを制限する方法を見てきました。 また、privateとマークされたコンストラクターとprotectedとマークされたコンストラクターの違いについても説明しました。

いつものように、ソースコードはGitHubにあります。