8.5 システム定義およびユーザー定義コンストラクタ

属性値コンストラクタとも呼ばれるシステム定義コンストラクタとユーザー定義コンストラクタには、様々な側面があります。

内容は次のとおりです。

8.5.1 属性値コンストラクタ

属性値コンストラクタとも呼ばれるシステム定義コンストラクタの場合、型の各属性値をコンストラクタに渡す必要があります。すると、例8-6に示すように、コンストラクタにより、新しいオブジェクト・インスタンスの属性にこれらの値が設定されます。

NEWは、コンストラクタへのコールの前にオプションで追加するキーワードです。このキーワードを追加することをお薦めします。

例8-6 コンストラクタによる属性値の設定

CREATE TYPE shape AS OBJECT (
    name VARCHAR2(30),
    area NUMBER);
/
CREATE TABLE building_blocks of shape;

-- attribute-value constructor: Sets instance attributes to the specified values
INSERT INTO building_blocks
  VALUES (
    NEW shape('my_shape', 4));

8.5.2 コンストラクタと型進化

属性値コンストラクタを使用することにより、型に対して独自のコンストラクタを定義する必要がなくなります。ただし、型に宣言されたすべての属性に対して値を指定する必要があり、指定しない場合はコンストラクタ・コールのコンパイルに失敗します。

ユーザー定義コンストラクタと異なり、特に属性値コンストラクタは暗黙的でコードに表示されないため、後で型を進化させる場合、この要件によって問題が発生することがあります。型の属性を変更した場合は、その型の属性値コンストラクタも変更されます。属性を追加した場合、更新された属性値コンストラクタは、新しい属性に対する値が指定されるものとみなします。それ以外の場合は、既存のコード内の属性値コンストラクタへのコールが失敗します。

関連項目:

型進化

8.5.3 ユーザー定義コンストラクタの利点

ユーザー定義コンストラクタは、属性値コンストラクタと異なり、型の各属性に対する値を明示的に設定する必要がありません。

ユーザー定義コンストラクタは、任意の型の任意の数の引数を取ることができ、これらの引数は型の属性に直接マップする必要がありません。コンストラクタを定義する場合、属性を任意の適切な値に初期化できます。値が指定されない属性は、システムによってNULLに初期化されます。

たとえば、属性を追加して型を進化させる場合、その型に対するユーザー定義コンストラクタへのコールは変更する必要がありません。型を進化させる場合、ユーザー定義コンストラクタは自動的に変更されないため、シグネチャは同じままです。ただし、新しい属性をNULLに初期化しない場合は、コンストラクタの定義の変更が必要になることがあります。

8.5.4 ユーザー定義コンストラクタの定義および実装

ユーザー定義コンストラクタは、通常のメソッドと同様に型本体に定義します。ユーザー定義コンストラクタの宣言および定義は、CONSTRUCTOR FUNCTION句を使用し、RETURN SELF AS RESULT句で終了します。

型のコンストラクタには、型と同じ名前を使用する必要があります。例8-7では、shape型の2つのコンストラクタ・ファンクションを定義します。例に示すとおり、異なるシグネチャを持つ複数のバージョンを定義することにより、ユーザー定義コンストラクタをオーバーロードできます。

例8-7 ユーザー定義コンストラクタの定義および実装

CREATE TYPE shape AS OBJECT (
    name VARCHAR2(30),
    area NUMBER,
    CONSTRUCTOR FUNCTION shape(SELF IN OUT NOCOPY shape, name VARCHAR2)
                               RETURN SELF AS RESULT,
    CONSTRUCTOR FUNCTION shape(SELF IN OUT NOCOPY shape, name VARCHAR2, 
                               area NUMBER) RETURN SELF AS RESULT
) NOT FINAL;
/

CREATE TYPE BODY shape AS
    CONSTRUCTOR FUNCTION shape(SELF IN OUT NOCOPY shape, name VARCHAR2) 
                               RETURN SELF AS RESULT IS
    BEGIN
        SELF.name := name;
        SELF.area := 0;
        RETURN;
    END;
    CONSTRUCTOR FUNCTION shape(SELF IN OUT NOCOPY shape, name VARCHAR2, 
                                area NUMBER) RETURN SELF AS RESULT IS
    BEGIN
        SELF.name := name;
        SELF.area := area;
        RETURN;
    END;
END;
/

ユーザー定義コンストラクタには、暗黙的な第1パラメータSELFがあります。ユーザー定義コンストラクタの宣言では、このパラメータの指定はオプションです。指定する場合、このパラメータのモードはIN OUTとして宣言する必要があります。

必須句RETURN SELF AS RESULTによって、戻されるインスタンスの最も狭い意味での型は、必ずSELF引数の最も狭い意味での型と同じになります。コンストラクタの場合、この型は、コンストラクタが定義された型です。shapeコンストラクタへのコールに対するSELF引数の最も狭い意味での型がshapeの場合、この句によってshapeコンストラクタからはshapeのサブタイプのインスタンスではなくshapeのインスタンスが必ず戻されます。

コンストラクタ・ファンクションがコールされると、システムはSELF引数の属性をNULLに初期化します。例8-7SELF.nameなど、ファンクション本体で初期化される後続の属性名は、コンストラクタ・ファンクションの引数の名前と区別するために、SELFで修飾できます(これらの名前が同一の場合)。引数の名前が異なる場合、この修飾は不要です。

例に示すとおり、ファンクション本体には、明示的なreturn;を含める必要があります。ただし、returnキーワードの後に、return式を続けることはできません。システムは、新しく作成されたSELFインスタンスを自動的に戻します。

ユーザー定義コンストラクタは、PL/SQL、CまたはJavaで実装できます。

8.5.5 オーバーロードおよび隠蔽されるコンストラクタ

他の型のメソッドと同様、ユーザー定義コンストラクタもオーバーロードできます。

ユーザー定義コンストラクタは継承されないため、スーパータイプで定義されたユーザー定義コンストラクタはサブタイプに隠蔽できません。ただし、ユーザー定義コンストラクタのシグネチャが属性値コンストラクタのシグネチャと完全に一致する場合、ユーザー定義コンストラクタはその型の属性値コンストラクタを隠蔽し、これを置き換えます。

シグネチャが一致するには、ユーザー定義コンストラクタのパラメータ(暗黙的なSELFパラメータの後)の名前と型が、その型の属性の名前と型と同じである必要があります。ユーザー定義コンストラクタのパラメータ(暗黙的なSELFパラメータの後)のモードは、必ずINにしてください。

同じ名前とシグネチャを持つユーザー定義コンストラクタによって属性値コンストラクタが隠蔽されていない場合は、継続して属性値コンストラクタをコールできます。

たとえば、属性の追加により型が進化する場合は、型の属性値コンストラクタのシグネチャもそれに対応して変更することに注意してください。これによって、以前に隠蔽されていた属性値コンストラクタが再度使用可能になります。

8.5.6 ユーザー定義コンストラクタのコール

他のすべてのファンクションと同様にユーザー定義コンストラクタをコールし、通常のファンクションが使用できるすべての場所で使用できます。

SELF引数は、暗黙的に渡され、明示的に渡されません。つまり、次のように使用することはできません。

NEW constructor(instance, argument_list)

CREATEまたはALTER TABLE文のDEFAULT句には、ユーザー定義コンストラクタは指定できませんが、属性値コンストラクタは指定できます。属性値コンストラクタの引数には、PL/SQLファンクションまたは他の列(疑似列LEVELPRIORROWNUMなど)への参照、あるいは完全に指定されていない日付定数の参照を含めることはできません。CHECK制約式についても同様で、表の作成中または変更中に、属性値コンストラクタはCHECK制約式の一部として使用できますが、ユーザー定義コンストラクタは使用できません。

SQLでは、引数を持たないコンストラクタ・コールについても、カッコを付ける必要があります。PL/SQLでは、引数を持たないコンストラクタを起動する場合、カッコはオプションです。ただし、カッコを付けた方が、コンストラクタ・コールがファンクション・コールであることがより明確になります。次のPL/SQL例では、新しいshapeを作成するためのコンストラクタ・コールのカッコが省略されています。

shape s := NEW my_schema.shape;

NEWキーワードおよびスキーマ名はオプションで使用してください。

例8-8では、例8-7で作成した型の下にサブタイプを作成し、ユーザー定義コンストラクタをコールする例を示します。

例8-8 ユーザー定義コンストラクタのコール

-- Requires Ex. 8-8
CREATE TYPE rectangle UNDER shape (
    len NUMBER,
    wth NUMBER,
    CONSTRUCTOR FUNCTION rectangle(SELF IN OUT NOCOPY rectangle,
        name VARCHAR2, len NUMBER, wth NUMBER) RETURN SELF as RESULT,
    CONSTRUCTOR FUNCTION rectangle(SELF IN OUT NOCOPY rectangle,
        name VARCHAR2, side NUMBER) RETURN SELF as RESULT);
/
SHOW ERRORS
CREATE TYPE BODY rectangle IS 
    CONSTRUCTOR FUNCTION rectangle(SELF IN OUT NOCOPY rectangle,
        name VARCHAR2, len NUMBER, wth NUMBER) RETURN  SELF AS RESULT IS
    BEGIN 
        SELF.name := name;
        SELF.area := len*wth;
        SELF.len := len;
        SELF.wth := wth;
        RETURN ;
    END;
    CONSTRUCTOR FUNCTION rectangle(SELF IN OUT NOCOPY rectangle,
        name VARCHAR2, side NUMBER) RETURN  SELF AS RESULT IS
    BEGIN 
        SELF.name := name;
        SELF.area := side * side;
        SELF.len := side;
        SELF.wth := side;
        RETURN ;
    END;
END;
/

CREATE TABLE shape_table OF shape;
INSERT INTO shape_table VALUES(shape('shape1')); 
INSERT INTO shape_table VALUES(shape('shape2', 20)); 
INSERT INTO shape_table VALUES(rectangle('rectangle', 2, 5)); 
INSERT INTO shape_table VALUES(rectangle('quadrangle', 12, 3));
INSERT INTO shape_table VALUES(rectangle('square', 12));

次の問合せでは、shape_table内の行を選択します。

SELECT VALUE(s) FROM shape_table s;
VALUE(S)(NAME, AREA)
---------------------------------------------
SHAPE('shape1', 0)
SHAPE('shape2', 20)
RECTANGLE('rectangle', 10, 2, 5)
RECTANGLE('quadrangle', 36, 12, 3)
RECTANGLE('square', 144, 12, 12)

次のPL/SQLコードは、コンストラクタをコールします。

s shape := NEW shape('void');

8.5.7 SQLJオブジェクト型のコンストラクタ

SQLJオブジェクト型は、JavaクラスにマップされるSQLオブジェクト型です。SQLJオブジェクト型は属性値コンストラクタを持ちます。このオブジェクト型は、参照されたJavaクラスでコンストラクタにマップされるユーザー定義コンストラクタを持つこともできます。

例8-9 SQLJオブジェクトの作成

CREATE TYPE address AS OBJECT 
   EXTERNAL NAME 'university.address' LANGUAGE JAVA
   USING SQLData(
     street   VARCHAR2(100) EXTERNAL NAME 'street',
     city     VARCHAR2(50)  EXTERNAL NAME 'city',
     state    VARCHAR2(50)  EXTERNAL NAME 'state',
     zipcode  NUMBER        EXTERNAL NAME 'zipcode',
    CONSTRUCTOR FUNCTION address (SELF IN OUT NOCOPY address, street VARCHAR2,
                                  city VARCHAR2, state VARCHAR2, zipcode NUMBER)
      RETURN SELF AS RESULT AS LANGUAGE JAVA
      NAME  'university.address (java.lang.String, java.lang.String,
                      java.lang.String, int) return address');
/

シリアル化表現のSQLJ型は、ユーザー定義コンストラクタのみ持つことができます。SQLJ型のオブジェクトの内部表現は、SQLにとって不透明であるため、SQLJ型に属性値コンストラクタは使用できません。