コンストラクタとファクトリー

この章では Curl® 言語におけるコンストラクタおよびファクトリーについて説明します。

コンストラクタ

要約:
  • コンストラクタはクラスのインスタンスを初期化します。
  • クラスに複数のコンストラクタを含めることができます。
  • コンストラクタの引数は、クラスのインスタンスを作成するための引数です。
  • サブクラスはコンストラクタを継承しません。
コンストラクタはクラスのインスタンスを初期化します。インスタンスの作成時にはコンストラクタは明示的に呼び出されません。コンストラクタは自動的に呼び出され、オブジェクトのインスタンス化の引数がコンストラクタの引数として渡されます。つまりコンストラクタの引数はクラスのインスタンスを作成するための引数になります。
コンストラクタは値を返しません。ただし、クラスのインスタンスを作成する (つまりコンストラクタを間接的に呼び出す) 呼び出しはオブジェクトを返します。
クラスに複数のコンストラクタを含めることができます。複数のコンストラクタにそれぞれ異なるアクセス属性を指定することができます。
クラスはコンストラクタを継承しません。つまり、1 つまたは複数のコンストラクタを持つクラスを継承するサブクラスを作成しても、結果のサブクラスはこれらのコンストラクタを継承しません。したがってサブクラスを記述する場合はサブクラス用の新しいコンストラクタも記述する必要があります。
クラスにアクセスできるコードは、そのクラスのインスタンスであるオブジェクトを理解して操作することができます。ただし、クラスのコンストラクタにアクセスできるコードだけがそのクラスのインスタンスを作成できます。クラスとそのコンストラクタのアクセス属性はたいてい同じなので、クラスにアクセスできるコードはそのクラスのインスタンスを作成することもできます。もちろんコンストラクタとそのクラスに異なるアクセス属性を指定することもできます。たとえば、 public アクセス属性のクラスが package アクセス属性のコンストラクタを持つ場合があります。このような場合、コンストラクタにアクセスできないコードはそのクラスのインスタンスを作成できませんが、クラス型を理解してこれを操作することは可能です。他の(アクセスが許可された)コードを呼び出してクラスのインスタンスを作成してこれを返すことができます。
コンストラクタの構文を次に示します。
構文:{constructor [access] [modifiers] {constructor-name [arg-list]}
body
}
説明:
accessアクセス属性です。有効な属性は publiclibrarypackageprotected および private です。すべてのコードがコンストラクタにアクセスできるようにするには public を使用します。クラスと同じパッケージ内のコードだけアクセスできるように制限 するには package を使用します。 protected は、同じパッケージまたはサブクラスの中のコードにアクセスを制限する時に使用してください。 private は、同じクラス内のコードにアクセスを制限する時に使用します。 library アクセス属性の詳細については library をご覧下さい。 access を指定しない場合、アクセス属性は既定の package になります。
modifiers
オプションの修飾子のリストです。コンストラクタでは implicitinline および deprecated だけが有効な修飾子です。複数の修飾子を指定する場合は、空白文字を使用して修飾子を区切ります。
implicit を使用すると、コンストラクタはその引数の型からクラスの型に値を暗黙的に変換できるようになります。暗黙的なコンストラクタの詳細は implicit キーワードの説明を参照してください。inline を使用すると、コンストラクタの使用をメソッドの実際のコードに置き換えるようにコンパイラに指示します。deprecated を使用すると、このコンストラクタを新しいコードで使用することは推奨されなく、将来は削除される可能性があることを示します。
constructor-nameコンストラクタの名前です。default という名前には特別な意味があり、呼び出し側がコンストラクタ名を指定せずにクラスのインスタンスを作成しようとするときに Curl® RTE は自動的にこの default コンストラクタを使用します。
arg-listカンマで区切られたコンストラクタの引数リストです。リスト引数と残余引数を指定できます。引数には位置引数とキーワード引数があります。さまざまなタイプの引数の詳細については「引数」のセクションを参照してください。
bodyコンストラクタの本体です。本体には変数および式の定義を含めることができます。
次のコード例では、クラス A にはフィールドを初期化する既定のコンストラクタがあります。

例: コンストラクタの使用 (パート 1)
|| Declare a class with an int field.
{define-class public A
  field public x:int = 9

  || The constructor takes one argument, an integer,
  || and uses that argument to set the field.
  {constructor {default x:int}
    set self.x = x
  }
}

|| Declare a variable of type A and initialize it with
|| an instance of the class A.  The argument (42) is
|| passed to the constructor, which then assigns the
|| argument to self.x.
{let a:A = {A 42}}

|| Display the value of the field in the resulting
|| object.
{value a.x}
次のコードでは、クラス B にはコンストラクタがありません。ただし、クラス C には CB から継承するフィールドを初期化する、既定のコンストラクタがあります。

例: コンストラクタの使用 (パート 2)
|| Declare an abstract class with an int field.
|| Remember a class need not include a constructor.
{define-class public abstract B
  field public x:int = 9
}

|| Declare a class that inherits from B.
{define-class package C {inherits B}

  || The constructor takes one argument, an integer,
  || and uses that argument to set the field.
  {constructor {default x:int}
    set self.x = x
  }
}

|| Declare a variable of type C and initialize it with
|| an instance of the class C.  The argument (42) is
|| passed to the constructor, which then assigns the
|| argument to self.x (which is the field that C
|| inherits from B).
{let c:C = {C 42}}

|| Display the value of the field in the resulting
|| object.
{value c.x}

コンストラクタの呼び出し

要約:
  • クラスのインスタンスを作成するには、コンストラクタを暗黙的に呼び出します。
  • サブクラスのコンストラクタ内からコンストラクタを明示的に呼び出すには construct-super を使用します。
  • コンストラクタには、その直属のスーパークラスのそれぞれへの呼び出しを含める必要があります。
クラスのインスタンスを作成するときには通常 new 演算子を使用します。たとえば、 Foo クラスのインスタンスは {new Foo} または単に {Foo} を呼び出すことによって作成できます。これは、default という名前のコンストラクタを暗黙的に呼び出すことになります。
既定のコンストラクタ以外のコンストラクタを呼び出してクラスのインスタンスを作成する場合は、次の構文を使用できます。
{class-name.constructor-name [arguments]}
ここで、class-name はクラス名、constructor-name はコンストラクタ名、arguments は呼び出しの引数のリストです。たとえば、次のコードは新しい Foo オブジェクトを作成し、from-String という名前のコンストラクタを呼び出して初期化します。
{Foo.from-String "hello"}
各コンストラクタには、その直属のスーパークラスのそれぞれへの呼び出しを含める必要があります。直属のスーパークラスとは、現在のクラスが直接継承するクラスのことです。
サブクラスに 1 つしかスーパークラスがない場合、スーパークラスのコンストラクタを呼び出すには次の構文を使用します。
{construct-super[.constructor-name] [arguments]}
ここで、constructor-name はコンストラクタ名で、arguments は呼び出しの引数リストです。既定のコンストラクタを呼び出す場合に constructor-name を指定する必要はありません。次に例を示します。
|| Calling the default constructor of a superclass
|| with no arguments.
{construct-super}

|| Calling the my-constructor constructor of a
|| superclass with no arguments.
{construct-super.my-constructor}
クラスが複数のスーパークラスを継承する場合は、次の構文を使用して各スーパークラスのコンストラクタを呼び出します。
{construct-super.class-name[.constructor-name] [arguments]}
ここで、class-name はスーパークラスの名前です。次に例を示します。
|| Calling the default constructor of the my-class
|| superclass with no arguments.
{construct-super.my-class}

|| Calling the my-constructor constructor of the
|| my-class superclass with no arguments.
{construct-super.my-class.my-constructor}
Curl 言語では、条件式の内部からコンストラクタへの呼び出しを含めることもできます。可能な場合は、Curl® コンパイラはコンパイル時に各スーパークラスが 1 度だけ初期化されることを確認します。確認できない場合でも RTE はこれを試みます。

2 次 コンストラクタ

要約:
  • 共有クラスのそれぞれのコンストラクタに対応する 2 次 コンストラクタを指定する必要があります。
  • 共有スーパークラスのコンストラクタを呼び出す際には、最初にコンストラクタが、次に対応する 2 次 コンストラクタが呼び出されます。
  • 2 次 コンストラクタのそれぞれには、対応する 1 次 コンストラクタと同じ名前と引数を指定する必要があります。
共有クラスとは、サブクラスによって複数回継承できるクラスのことを指します。多重継承が可能であるため、スーパークラスを何度も継承することができます。このような場合、コンパイラで共有スーパークラスのコンストラクタが 1 度だけ呼び出されるようにします。共有スーパークラスのコンストラクタが複数回呼ばれた場合、予期せぬ結果や望ましくない効果が生じる可能性があります。適切な動作がサポートされるように Curl 言語は 2 次 コンストラクタの機能を提供しています。
共有クラスを定義する際に、クラスの 1 次 コンストラクタのそれぞれに対応する 2 次 コンストラクタを指定する必要があります。2 次 コンストラクタにはそれぞれ対応する 1 次 コンストラクタと同じ名前、同じアクセス属性、同じ引数を指定することも必要です。つまり、たとえば共有クラスに defaultfrom-String という 2 つのコンストラクタがある場合、そのクラスには同じ引数を取る defaultfrom-String という 2 つの 2 次 コンストラクタもなければなりません。
コンパイラが共有スーパークラスのコンストラクタの呼び出しを検出すると、最初に実際のコンストラクタを呼び出し、次に対応する 2 次 コンストラクタを呼び出します。2 次 コンストラクタを直接呼び出すことはできません。2 次 コンストラクタを呼び出す唯一の方法は、construct-super を使用してスーパークラスのコンストラクタ(すでに呼び出されている)を呼び出すことです。
共有クラスの定義にコンストラクタが含まれていない場合は、2 次コンストラクタを指定する必要はありません。また、 (1) shared 修飾子で定義されていないクラス、または (2) 対応する 1 次 コンストラクタが含まれていないクラスに 2 次コンストラクタを作成することはできません。
2 次 コンストラクタの構文を次に示します。
構文:{secondary-constructor [access] [modifiers] {constructor-name [arg-list]}
body
}
説明:
access対応する 1 次コンストラクタのアクセス属性です。
modifiers対応する 1 次コンストラクタと同じ修飾子のリストである必要があります。
constructor-name対応する 1 次コンストラクタの名前です。
arg-list対応する 1 次コンストラクタの引数のリストです。
body2 次 コンストラクタの本体です。本体には変数および式の定義を含めることができます。
次に例を示します。
|| Declare a shared class.  For each constructor in a
|| shared class, there must be a corresponding secondary
|| constructor.
{define-class shared D

  field constant name:String

  || The default constructor takes two arguments, an integer
  || and a floating-point number, and uses them to set the
  || name field.
  {constructor {default x:int, y:float}
    set self.name = {format "%s/%s", x, y}
  }

  || If the name field is already set (because the
  || corresponding constructor was already called), do
  || nothing.  The corresponding secondary constructor
  || must have the same name and list of arguments.
  {secondary-constructor {default x:int, y:float}
    || Do nothing.
  }
}

ファクトリー

要約:
  • 抽象クラスも含め、任意のクラスに 1 つまたは複数のファクトリーを作成することができます。
  • ファクトリーを使用して、クラスの既定のインスタンス化をオーバーライドします。
  • ファクトリーは 1 つの値を返します。これは、ファクトリーが含まれるクラスのインスタンスか、そのクラスのサブクラスのインスタンスです。
  • サブクラスはファクトリーを継承しません。
クラスには 1 つまたは複数のファクトリーを含めることができます。ファクトリーは新しいオブジェクトを生成して返します。ファクトリーが返すオブジェクトがクラスまたはサブクラスのインスタンスになることもあります。
通常、クラスのインスタンスの作成時には以下の一連のイベントが実行されます。
クラスにファクトリーを加えてこれらのイベントをオーバーライドすることができます。クラスのインスタンスを作成する普通の方法は、Foo {new Foo} または単に {Foo} を呼び出すことです。これは default という名前のファクトリーが存在する場合はそれを使用します。クラス プロシージャの呼び出しと同じように、任意のファクトリーを明示的に呼び出すこともできます。たとえば、{Foo.from-String "hello"}Foo クラスの from-String というファクトリーを呼び出します。もちろん、メモリの割り当てやオブジェクトの初期化などの問題をファクトリーで処理する必要があります。
通常、クラスのインスタンスを作成すると引数のリストがコンストラクタに渡され、コンストラクタがオブジェクトを初期化します。代わりにクラス ファクトリーが呼ばれた場合、引数リストはファクトリーに渡されます。もちろん、ファクトリーは次にコンストラクタを呼び出してオブジェクトを作成することができます。
ファクトリーの戻り値は 1 つだけです。戻り値のデータ型は、そのクラスのファクトリーまたはサブクラスが含まれているクラスである必要があります。ファクトリーは既存のオブジェクトを返すことができます。たとえば、UniqueString クラスに既定のファクトリーを作成し、ハッシュ テーブルを検索して適切な UniqueString オブジェクトがすでに存在するかどうかを確認し、存在する場合はこのオブジェクトを返し、存在しない場合はオブジェクトを作成することができます。
抽象クラスも含めて、任意のクラスでファクトリーを作成することができます。これは、ファクトリーを使用すると抽象クラスのインスタンスが作成できるという意味もあります。ただし、抽象クラスのインスタンスは直接作成できないので、ファクトリーはそれが含まれているクラスのサブクラスを返す必要があります。
コンストラクタと同様にサブクラスはファクトリーを継承しません。つまり、ファクトリーを持つクラスを継承するサブクラスを作成しても、結果のサブクラスはファクトリーを継承しません。
メソッドと同様にファクトリーにはアクセス属性があります。アクセス属性は、ファクトリーにアクセスできるコードを定義するものです。一般的に、ファクトリーのアクセス属性はクラスのアクセス属性と同じにすることを推奨します。これにより、クラスにアクセスできるコードは必ずそのクラスのインスタンスを作成できるようになります。
ファクトリーの構文を次に示します。
構文:{factory [access] [modifiers] {factory-name [arg-list]}:return-value
body
}
説明:
accessアクセス属性です。有効な属性は publiclibrarypackageprotected および private です。すべてのコードがコンストラクタにアクセスできるようにするには public を使用します。クラスと同じパッケージ内のコードだけアクセスできるように制限するには package を使用します。library アクセス属性についての詳細は library をご覧下さい。access を指定しない場合、アクセス属性は既定の package になります。
modifiers
オプションの修飾子のリストです。ファクトリーでは implicitinline および deprecated だけが有効な修飾子です。複数の修飾子を指定する場合は、空白文字を使用して修飾子を区切ります。
implicit を使用すると、ファクトリーの引数の型からクラスの型に値が暗黙的に変換できるようになります。暗黙的なファクトリーの詳細は implicit キーワードの説明を参照してください。inline を使用すると、ファクトリーの使用をメソッドの実際のコードに置き換えるようにコンパイラに指示します。deprecated を使用すると、このコンストラクタを新しいコードで使用することは推奨されなく、将来は削除される可能性があることを示します。
factory-nameファクトリーの名前です。default という名前には特別な意味があり、呼び出し側がファクトリー名を指定せずにクラスのインスタンスを作成しようとすると、default ファクトリーが自動的に使用されます。
arg-listカンマで区切られたファクトリーの引数リストです。リスト引数と残余引数を指定できます。引数には位置引数とキーワード引数があります。コンマを使用して指定する引数を区切ります。さまざまなタイプの引数の詳細については「引数」のセクションを参照してください。
return-valueファクトリーの戻り値のデータ型です。ファクトリーの戻り値は 1 つだけです。戻り値のデータ型は、そのクラスのファクトリーまたはサブクラスが含まれているクラスである必要があります。
bodyファクトリーの本体です。本体には変数および式の定義を含めることができます。
たとえば、次のコードはファクトリーを含む抽象クラスを示しています。ファクトリーは現在のクラスのサブクラスのインスタンスを返します。

例: ファクトリーの使用
|| Declare an abstract class with a factory.  Remember
|| that a class need not include a constructor.
{define-class public abstract E

  || The factory returns an object whose type is this
  || class.
  {factory public {default}:E
    || This factory actually returns an instance of F,
    || which is a subclass of E.
    {return {new F}}
  }

  || The getter returns a string indicating the type
  || of the object.
  {getter public {name}:String
    {return "My type is E"}
  }
}

|| Declare a class that inherits from E.  Remember that
|| subclasses do not inherit factories.
{define-class package F {inherits E}
  || The getter returns a string indicating the type
  || of the object.
  {getter public {name}:String
    {return "My type is F"}
  }
}

|| Declare a variable of type E and initialize it
|| with an instance of the class E.  The factory
|| in E returns an instance of the class F.
{let e:E = {new E}}

|| Display the value of the name getter for the
|| resulting object.
{value e.name}
ファクトリーを使用して抽象クラスのインスタンスを作成することができます。ファクトリーの戻り値は具象サブクラスのインスタンスでなければなりません。たとえば、サウンド再生のインターフェイスを定義する SoundSource という抽象クラスがあり、各種のサウンド ファイルを再生する CompactDisksWaveFilesMP3Files などの具象サブクラスがあるとします。ユーザーが簡単に操作できるインターフェイスを提供するために、これらのサウンド ファイルについて SoundSource のインスタンスをユーザーが作成できるようにします。これを実現するには、ファクトリーを追加して適切な具象サブクラスのインスタンスを返します。