2.3 SQLオブジェクト型の継承

SQLオブジェクト継承は、型階層を構成するオブジェクト型の系図に基づいています。型の階層は、スーパータイプという親オブジェクト型と、親から導出された、サブタイプという1つ以上のレベルの子オブジェクト型で構成されます。

内容は次のとおりです。

2.3.1 SQLオブジェクト型の継承について

継承は、階層内のサブタイプを対応するスーパータイプに接続するメカニズムです。

サブタイプは、その親タイプの属性およびメソッドを自動的に継承します。また、継承リンクも保持されます。サブタイプは、親の属性またはメソッドの変更を自動的に取得します。スーパータイプの属性またはメソッドが更新されると、サブタイプにおいてもその更新が行われます。

注意:

単一継承のみがサポートされています。したがって、サブタイプは1つのスーパータイプからのみ直接導出でき、複数のスーパータイプから導出することはできません。

型階層内でオブジェクト型を使用すると、顧客などのエンティティをモデル化したり、元の型を基にして顧客の特化された様々なサブタイプを定義することができます。このように定義した後、階層に対して操作を行い、それぞれの型を実装させ、専用の方法で操作を実行できます。

2.3.2 スーパータイプおよびサブタイプ

サブタイプは、スーパータイプから直接または他のサブタイプを介して間接的に導出できます。

スーパータイプは、兄弟関係にあるサブタイプを複数持つことができますが、サブタイプが持つことができる直系の親スーパータイプは1つのみです(単一継承)。

図2-1 型階層内のスーパータイプおよびサブタイプ

図2-1の説明が続きます
「図2-1 型階層内のスーパータイプおよびサブタイプ」の説明

スーパータイプからサブタイプを導出するには、親から継承したセットに新しい属性およびメソッドを追加するスーパータイプの特殊な異型を定義するか、継承したメソッドを再定義(オーバーライド)します。たとえば、person_typオブジェクト型からは、特殊な異型であるstudent_typemployee_typを導出できます。これらのサブタイプは、根本的にはperson_typですが、特殊な種類の個人です。サブタイプが親から受け取った属性またはメソッドに加えられたなんらかの変更が、サブタイプと親のスーパータイプを区別するものとなります。

継承したメソッドを再定義しない場合、サブタイプには常に親タイプが持つ属性およびメソッドの同じコア・セットと、自身が追加する属性とメソッドが含まれます。person_typオブジェクト型がidnonameおよびphoneという3つの属性と、メソッドget_idno()を持つ場合、person_typから導出されたオブジェクト型は、これらと同じ3つの属性とメソッドget_idno()を持つことになります。person_typの定義を変更すると、サブタイプの定義も変更されます。

サブタイプは、次のようにキーワードUNDERを使用して作成します。

CREATE TYPE student_typ UNDER person_typ

サブタイプの属性またはメソッドは、次の方法で特化します。

  • 親スーパータイプが持たない新しい属性を追加します。

    たとえば、majorの属性を追加して、person_typの特殊な種類としてstudent_typを特化します。サブタイプは、親から継承した属性の型の削除や変更はできず、新しい属性を追加するのみです。

  • 親スーパータイプにない新しいメソッドを追加します。

  • サブタイプのバージョンで親のバージョンとは異なるコードが実行されるように、サブタイプが継承するメソッドの一部の実装を変更します。

    たとえば、ellipseオブジェクトがメソッドcalculate()を定義するとします。ellipse_typの2つのサブタイプであるcircle_typおよびsphere_typが、それぞれ別の方法でこのメソッドを実装することができます。

スーパータイプとそのサブタイプの間の継承関係が、オブジェクトの能力の大半をもたらす原因になっていると同時に、オブジェクトを複雑にする原因にもなっています。

スーパータイプのメソッドが変更でき、再コンパイルするのみですべての下位サブタイプにこの変更を反映させることができるというのは、非常に強力な機能です。しかし、それは同時に、型を特化するか、それともメソッドを再定義するかを考慮する必要があるということでもあります。また、階層内の任意の型を表や列に入れることができるというのは強力な機能ですが、特定の場合にこれを行うかどうかを決定する必要があります。さらに、必要な範囲の型のみが型階層から選択されるように、DML文や問合せに制約を付ける必要があることもあります。

関連項目:

2.3.3 継承のためのFINALとNOT FINALの型およびメソッド

オブジェクト型は継承可能にすることができ、メソッドはオーバーライド可能であると定義されている場合はオーバーライドできます。

継承可能にするオブジェクト型またはメソッドの場合、定義でそれが継承可能であることを指定する必要があります。型とメソッドのどちらにも、継承可能かどうかを決定するためにキーワードFINALまたはNOT FINALを使用します。

  • オブジェクト型: オブジェクト型を継承可能にして、そのオブジェクト型からサブタイプを導出できるようにするには、オブジェクト定義でそのように指定する必要があります。

    NOT FINALは、サブタイプを導出できることを意味します。FINAL(デフォルト)は、この型からサブタイプを導出できないことを意味します。

  • メソッド: 定義でオーバーライドが可能かどうかを指定する必要があります。

    NOT FINAL(デフォルト)は、メソッドをオーバーライドできることを意味します。FINALは、サブタイプは自身の実装を組み込むことでメソッドをオーバーライドすることはできないことを意味します。

2.3.3.1 FINALメンバー・ファンクションが含まれるNOT FINALとしてのオブジェクト型の作成

例2-12のように、FINALメンバー・ファンクションが含まれるNOT FINALオブジェクト型を作成できます。

例2-12 FINALメンバー・ファンクションが含まれるNOT FINALとしてのオブジェクト型の作成

DROP TYPE person_typ FORCE;
-- above necessary if you have previously created object

CREATE OR REPLACE TYPE person_typ AS OBJECT (
   idno           NUMBER,
   name           VARCHAR2(30),
   phone          VARCHAR2(20),
   FINAL MAP MEMBER FUNCTION get_idno RETURN NUMBER)
NOT FINAL;
/

2.3.3.2 NOT FINALオブジェクト型の作成

NOT FINALとしてのオブジェクト型を作成できます。

例2-13では、person_typNOT FINAL型として宣言して、person_typのサブタイプを定義できるようにしています。

例2-13 NOT FINALとしてのperson_typオブジェクト型の作成

DROP TYPE person_typ FORCE;
-- above necessary if you have previously created object

CREATE OR REPLACE TYPE person_typ AS OBJECT (
   idno           NUMBER,
   name           VARCHAR2(30),
   phone          VARCHAR2(20)) 
NOT FINAL;
/

2.3.4 NOT FINALへのFINAL型の変更

継承を変更するには、ALTER TYPE文を使用してFINAL型をNOT FINAL型に、またはその逆に変更します。

次に示す文は、person_typをFINAL型に変更します。

ALTER TYPE person_typ FINAL;

型をNOT FINALからFINALに変更できるのは、ターゲット型がサブタイプを持たない場合のみです。

2.3.5 サブタイプの作成

サブタイプは、CREATE TYPE文を使用して作成します。この文は、UNDERキーワードにより、サブタイプの直系の親を指定します。

内容は次のとおりです。

2.3.5.1 親オブジェクトまたはスーパータイプ・オブジェクトの作成

親オブジェクトまたはスーパータイプ・オブジェクトは、CREATE TYPE文を使用して作成できます。

例2-14では、例2-15例2-18および例2-19のサブタイプ定義を示すために、親またはスーパータイプのperson_typオブジェクトを作成します。

例2-14show()に注意してください。続くサブタイプの各例では、OVERRIDINGキーワードにより、親タイプのshow()ファンクションが各サブタイプの指定にオーバーライドされています。

例2-14 親またはスーパータイプのperson_typオブジェクトの作成

DROP TYPE person_typ FORCE;
-- if created
CREATE OR REPLACE TYPE person_typ AS OBJECT (
 idno           NUMBER,
 name           VARCHAR2(30),
 phone          VARCHAR2(20),
 MAP MEMBER FUNCTION get_idno RETURN NUMBER,
 MEMBER FUNCTION show RETURN VARCHAR2)
 NOT FINAL;
/
 
CREATE OR REPLACE TYPE BODY person_typ AS
 MAP MEMBER FUNCTION get_idno RETURN NUMBER IS
 BEGIN
   RETURN idno;
 END;
-- function that can be overriden by subtypes
 MEMBER FUNCTION show RETURN VARCHAR2 IS
 BEGIN
   RETURN 'Id: ' || TO_CHAR(idno) || ', Name: ' || name;
 END;
 
END;
/

2.3.5.2 サブタイプのオブジェクトの作成

サブタイプは、スーパータイプの属性およびメソッドを継承します。

継承するものは次のとおりです。

  • スーパータイプに宣言または継承されているすべての属性

  • スーパータイプに宣言または継承されているすべてのメソッド

例2-15では、student_typオブジェクトをperson_typのサブタイプとして定義し、person_typで宣言または継承されているすべての属性およびperson_typで宣言または継承されているメソッドを継承します。

例2-15 UNDER句を使用したstudent_typサブタイプの作成

-- requires Ex. 2-14
CREATE TYPE student_typ UNDER person_typ (
   dept_id NUMBER,
   major VARCHAR2(30),
   OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2)
   NOT FINAL;
/
 
CREATE TYPE BODY student_typ AS
 OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2 IS
 BEGIN
    RETURN (self AS person_typ).show || ' -- Major: ' || major ;
 END;
 
END;
/

student_typの定義文は、2つの新しい属性(dept_idおよびmajor)を追加してperson_typを特化し、showメソッドをオーバーライドしています。サブタイプの中で宣言する新しい属性には、自身の型階層内の上位に位置するそのスーパータイプのいずれかに宣言されている属性またはメソッドとは別の名前を持たせる必要があります。

2.3.5.3 一般的な起動

一般的な起動は、特定のサブタイプ・メンバー・メソッドではなく、スーパータイプまたは親タイプのメソッドを起動するメカニズムを提供します。

例2-15は、次の構文を使用してこのことを示しています。

     (SELF AS person_typ).show

student_typ showメソッドは、person_typ showメソッドをコールして一般的な処理を実行した後で、そのperson_typ showメソッドによって戻された値に'--Major:'を追加するという独自の処理を実行します。このように、サブタイプのメソッドのオーバーライドは、対応する親タイプのメソッドのオーバーライドをコールし、一般的な処理を実行してから独自の処理を実行できます。

メソッドは、標準のメンバー・メソッド同様に起動されます。ただし、ASの後の型名は、式が評価する型の親タイプの型名である必要があります。

2.3.5.4 一般的な起動の使用

例2-16には、標準のメンバー・メソッド起動時の暗黙的自己引数に似た暗黙的SELF引数が示されています。この場合は、限定的なstudent_typ showメソッドではなく、person_typ showメソッドが起動されます。

例2-16 一般的な起動の使用

-- Requires Ex. 2-14 and 2-15
DECLARE
 myvar student_typ := student_typ(100, 'Sam', '6505556666', 100, 'Math');
 name VARCHAR2(100);
BEGIN
 name := (myvar AS person_typ).show; --Generalized invocation
END;
/ 

2.3.5.5 一般的な式の使用

メンバー・メソッドの起動と同様に、一般的な式もメソッドが明示的自己引数で起動される場合にサポートされます。

例2-17 一般的な式の使用

-- Requires Ex. 2-14 and 2-15
DECLARE
 myvar2 student_typ := student_typ(101, 'Sam', '6505556666', 100, 'Math');
 name2 VARCHAR2(100);
BEGIN
 name2 := person_typ.show((myvar2 AS person_typ)); -- Generalized expression
END;
/ 

この例で二重カッコを使用しているのは、((myvar2 AS person_typ))は解決が必要な式であると同時にshowファンクションのパラメータでもあるためです。

注意: コンストラクタ・メソッドの起動にこの構文は使用できません。また、この構文でASの後に示される型名は、メソッドが起動される式の型の親タイプの名前である必要があります。

この構文を使用できるのは、対応する親タイプのメンバー・メソッドのオーバーライドを起動する場合のみです。

2.3.5.6 複数のサブタイプの作成

型には、複数の子サブタイプを持たせることができ、これらのサブタイプにもサブタイプを持たせることができます。

例2-18では、例2-15で作成した既存のサブタイプstudent_typに加えて、person_typの下に別のサブタイプemployee_typを作成します。

例2-18 UNDER句を使用したemployee_typサブタイプの作成

-- requires Ex. 2-14

DROP TYPE employee_typ FORCE;
-- if previously created
CREATE OR REPLACE TYPE employee_typ UNDER person_typ (
    emp_id NUMBER, 
    mgr VARCHAR2(30),
    OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2);
/

CREATE OR REPLACE TYPE BODY employee_typ AS
  OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2 IS
  BEGIN
    RETURN (SELF AS person_typ).show|| ' -- Employee Id: ' 
           || TO_CHAR(emp_id) || ', Manager: ' || mgr ;
  END;
  
END;
/

2.3.5.7 別のサブタイプの下でのサブタイプの作成

別のサブタイプの下に、サブタイプを定義できます。

新しく定義されたサブタイプは、その親タイプが持つすべての属性およびメソッド(宣言されているもの、および継承されたもの)を継承します。例2-19では、例2-15で作成したstudent_typの下に新しいサブタイプpart_time_student_typを定義します。この新しいサブタイプは、student_typのすべての属性およびメソッドを継承し、別の属性number_hoursを追加します。

例2-19 UNDER句を使用したpart_time_student_typサブタイプの作成

CREATE TYPE part_time_student_typ UNDER student_typ (
  number_hours NUMBER,
  OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2);
/

CREATE TYPE BODY part_time_student_typ AS
  OVERRIDING MEMBER FUNCTION show RETURN VARCHAR2 IS
  BEGIN
    RETURN (SELF AS person_typ).show|| ' -- Major: ' || major ||
           ', Hours: ' || TO_CHAR(number_hours);
  END;
  
END;
/

2.3.5.8 スーパータイプおよびサブタイプのオブジェクトを持つ表の作成

スーパータイプおよびサブタイプのインスタンスを含む表を作成できます。

例2-20person_obj_tableに示すように、作成した表にデータを移入できます。

例2-20 オブジェクト表の代入可能な行への値の挿入

CREATE TABLE person_obj_table OF person_typ;

INSERT INTO person_obj_table 
  VALUES (person_typ(12, 'Bob Jones', '650-555-0130'));

INSERT INTO person_obj_table 
  VALUES (student_typ(51, 'Joe Lane', '1-650-555-0140', 12, 'HISTORY'));

INSERT INTO person_obj_table 
  VALUES (employee_typ(55, 'Jane Smith', '1-650-555-0144', 
                       100, 'Jennifer Nelson'));

INSERT INTO person_obj_table  
  VALUES (part_time_student_typ(52, 'Kim Patel', '1-650-555-0135', 14,
         'PHYSICS', 20));

次のように、表のスーパータイプとサブタイプについてshow()ファンクションをコールできます。

SELECT p.show() FROM person_obj_table p;

出力は次のようになります。

Id: 12, Name: Bob Jones
Id: 51, Name: Joe Lane -- Major: HISTORY
Id: 55, Name: Jane Smith -- Employee Id: 100, Manager: Jennifer Nelson
Id: 52, Name: Kim Patel -- Major: PHYSICS, Hours: 20

show()メソッドが表示するデータは、オブジェクトがスーパータイプかサブタイプか、またサブタイプのshow()メソッドがオーバーライドされているかどうかで異なることに注意してください。たとえば、Bob Jonesはperson_typ、つまりスーパータイプです。nameIdのみが表示されます。Joe Laneはstudent_typで、nameIdはスーパータイプのshow()ファンクションから提供され、majorはサブタイプのオーバーライドされたshow()ファンクションから提供されます。

2.3.6 NOT INSTANTIABLEの型とメソッド

型およびメソッドは、作成時にNOT INSTANTIABLEとして宣言できます。

NOT INSTANTIABLEの型とメソッドは、次のとおりです。

  • NOT INSTANTIABLE

    型がNOT INSTANTIABLEの場合、その型のインスタンスはインスタンス化できません。その型に(デフォルトまたはユーザー定義を問わず)コンストラクタはありません。これを型に使用するとすれば、その型をスーパータイプとしてのみ使用し、そこから特化されたサブタイプをインスタンス化する場合です。

  • NOT INSTANTIABLEメソッド

    インスタンス化不可のメソッドは、プレースホルダの役目を果します。これは宣言されますが、型の中に実装されません。すべてのサブタイプにおいてそれぞれ独自の方法でメソッドをオーバーライドする場合に、インスタンス化不可のメソッドを定義します。この場合、スーパータイプにメソッドを定義しても有益ではありません。

インスタンス化可能な型からインスタンス化不可の型への切替え、またその逆の切替えには、ALTER TYPE文を使用します。

インスタンス化不可のメソッドを含む型は、例2-21のように、その型自体をインスタンス化不可として定義する必要があります。

2.3.7 インスタンス化不可のオブジェクト型の作成

インスタンス化不可の継承メソッドが、サブタイプにおいても実装されない場合、スーパータイプと同様にサブタイプそのものをインスタンス化不可として定義する必要があります。

インスタンス化不可のサブタイプは、インスタンス化不可のスーパータイプの下に定義できます。

例2-21 NOT INSTANTIABLEとしてのオブジェクト型の作成

DROP TYPE person_typ FORCE;
-- if previously created
CREATE OR REPLACE TYPE person_typ AS OBJECT (
  idno           NUMBER,
  name           VARCHAR2(30),
  phone          VARCHAR2(20),
  NOT INSTANTIABLE MEMBER FUNCTION get_idno RETURN NUMBER)
  NOT INSTANTIABLE NOT FINAL;/

2.3.8 INSTANTIABLEへのオブジェクト型の変更

ALTER TYPE文により、インスタンス化不可の型をインスタンス化可能にすることができます。

例2-22では、ALTER TYPE文によってperson_typがインスタンス化可能になります。

例2-22 INSTANTIABLEへのオブジェクト型の変更

CREATE OR REPLACE TYPE person_typ AS OBJECT (
  idno           NUMBER,
  name           VARCHAR2(30),
  phone          VARCHAR2(20))
  NOT INSTANTIABLE NOT FINAL;/
ALTER TYPE person_typ INSTANTIABLE;

インスタンス化不可の型への変更

インスタンス化可能な型からインスタンス化不可の型に変更できる型は、その型を直接または別の型やサブタイプを介して間接的に参照する列、ビュー、表またはインスタンスを持たないものに限定されます。

インスタンス化不可の型をFINALとして宣言することはできません。これは実際に意味がありません。

2.3.9 オーバーロードおよびオーバーライドされるメソッド

サブタイプは自身が継承するメソッドを再定義できる他、同名のメソッドなど、新しいメソッドを追加することも可能です。

内容は次のとおりです。

「サブタイプの作成」の例および例8-7を参照してください。

2.3.9.1 メソッドのオーバーロード

継承されたメソッドと同名の新しいメソッドをサブタイプに追加することを、オーバーロードといいます。

名前が同じでシグネチャが異なるメソッドが同じユーザー定義型に存在する場合、それらのメソッドはオーバーロードと呼ばれます。

メソッド・シグネチャは、メソッドの名前、数値、型、およびメソッドの仮パラメータ(暗黙的なselfパラメータなど)の順序で構成されます。

様々な動作方法を用意するには、オーバーロードが役立ちます。たとえば、ellipseオブジェクトは、異なる形態の計算を可能にするためにcalculate()メソッドを別のcalculate()メソッドでオーバーロードできます。

1つの型にオーバーロードされたメソッドが複数ある場合、コンパイラはメソッド・シグネチャを使用して、どのメソッドをコールするかを判断します。

次の疑似コードのサブタイプcircle_typは、calculate()のオーバーロードを作成します。

CREATE TYPE ellipse_typ AS OBJECT (...,
MEMBER PROCEDURE calculate(x NUMBER, x NUMBER),
) NOT FINAL;
CREATE TYPE circle_typ UNDER ellipse_typ (...,
MEMBER PROCEDURE calculate(x NUMBER),
...);

circle_typには、2つのバージョンのcalculate()が含まれます。1つは、NUMBERパラメータを2つ持つ継承されたバージョンで、もう1つはNUMBERパラメータを1つ持つ新しく作成されたメソッドです。

2.3.9.2 メソッドのオーバーライドおよび隠蔽

あるサブタイプの動作をカスタマイズするために、継承されたメソッドを再定義することを、メンバー・メソッドの場合はオーバーライド、静的メソッドの場合は隠蔽といいます。

オーバーロードとは異なり、新しいメソッドは作成せず、キーワードOVERRIDINGを使用して既存のメソッドを再定義するだけです。

オーバーライドおよび隠蔽は、サブタイプにおいて継承メソッドにこれまでとは異なる動作をさせるために、継承メソッドを再定義することです。たとえば、ellipse_typスーパータイプから導出されたサブタイプcircle_typは、円の面積を求めるようにメンバー・メソッドcalculate()をカスタマイズするために、このメソッドをオーバーライドします。メソッドをオーバーライドする例は、「サブタイプの作成」を参照してください。

いずれの場合も、サブタイプで再定義されたメソッドのバージョンが、同じ名前およびシグネチャを持つ元のバージョンを隠し、サブタイプのインスタンスがメソッドを起動する際には、元のバージョンのかわりに新しいバージョンが実行されるという点では、オーバーライドと隠蔽は似ています。サブタイプ自身がサブタイプを持つ場合は、元のメソッドではなく、再定義されたメソッドを継承します。

オーバーライドの場合、実行するメソッドの正しいバージョンを動的に選択するために、システムは、メンバー・メソッドの暗黙的自己引数に含まれている型情報に依存します。隠蔽の場合、正しいバージョンはコンパイル時に識別されるため、動的ディスパッチは必要ありません。「動的メソッド・ディスパッチ」を参照してください。

メソッドをオーバーライドまたは隠蔽するには、メソッドのシグネチャを保持する必要があります。メソッドのオーバーロードは、すべて同じ名前を持つため、コンパイラはサブタイプのメソッドのシグネチャを使用して、置き換えられるスーパータイプのバージョンを識別します。

CREATE TYPE BODY文の中でOVERRIDINGキーワードを使用してオーバーライドを通知します。サブタイプが静的メソッドを隠蔽する場合、これは必要ありません。

次の疑似コードでは、サブタイプがメソッドcalculate()をオーバーライドしていることを通知しています。

CREATE TYPE ellipse_typ AS OBJECT (...,
MEMBER PROCEDURE calculate(),
FINAL MEMBER FUNCTION function_mytype(x NUMBER)...
) NOT FINAL;
CREATE TYPE circle_typ UNDER ellipse_typ (...,
OVERRIDING MEMBER PROCEDURE calculate(),
...);

この階層のダイアグラムは、図2-2を参照してください。

2.3.9.3 メソッドのオーバーライドの制限事項

メソッドのオーバーライドには、一定の制限事項があります。

  • オーバーライドできるメソッドは、スーパータイプでFINALとして宣言されていないもののみです。

  • オーダー・メソッドが出現する可能性があるのは、型階層のルート型のみで、サブタイプで再定義(オーバーライド)することはできません。

  • サブタイプの静的メソッドにより、スーパータイプのメンバー・メソッドを再定義することはできません。

  • サブタイプのメンバー・メソッドにより、スーパータイプの静的メソッドを再定義することはできません。

  • オーバーライドされているメソッドが、いずれかのパラメータのデフォルト値を持つ場合は、そのパラメータのデフォルト値と同じものを、オーバーライドするメソッドに持たせる必要があります。

2.3.10 動的メソッド・ディスパッチ

動的メソッド・ディスパッチとは、現在の型または指定された型から上方向に型階層を調べて、実行時にメソッド・コールを最も近い実装にディスパッチする方法のことです。

動的メソッド・ディスパッチは、メンバー・メソッドをオーバーライドするときにのみ使用可能であり、静的メソッドには適用されません。

メソッドのオーバーライドにより、型階層で同じメソッドの複数の実装を定義できます。次に示すellipse_typ型、circle_typ型、sphere_typ型の階層の中では、それぞれの型が異なる方法でcalculateメソッドを定義できます。

これらのメソッドの1つが起動されると、メソッドを起動したオブジェクト・インスタンスの型により、そのメソッドが使用する実装が決まります。次に、この実装にコールがディスパッチされ、実行されます。メソッド実装を選択するこのプロセスは、コンパイル時ではなく、実行時に処理されるため、仮想メソッド・ディスパッチまたは動的メソッド・ディスパッチと呼ばれます。

メソッド・コールは型階層を上方向に調べます。下方向には調べません。コールによりオブジェクト・インスタンスのメンバー・メソッドが起動される場合は、そのインスタンスの型が現在の型となり、その型で定義または継承されている実装が使用されます。コールにより型の静的メソッドが起動される場合は、その指定の型で定義または継承されている実装が使用されます。

関連項目:

サブプログラム・コールの解決方法の詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください

2.3.11 型階層内の型の代入

型階層内で型を扱うときは、最も一般的なレベル(全員の選択や更新など)で作業する必要がある場合があります。しかし、学生のみ、または学生以外の個人のみなど、特定のサブタイプのみを選択または更新する必要がある場合もあります。

全員を選択し、宣言された型がperson_typのオブジェクトのみでなく、宣言されたサブタイプがstudent_typまたはemployee_typのオブジェクトも返す(多様な性質を持つ)機能は、代入性と呼ばれます。宣言された型がスーパータイプである変数または列に、スーパータイプの下位のサブタイプの1つが使用できる場合、スーパータイプは代入可能です。

一般的に、型は代入可能です。オブジェクト属性、コレクション要素およびREFは代入可能です。REF、型またはperson_typ型のコレクションとして定義された属性には、person_typのインスタンス、そのインスタンスのインスタンス、またはperson_typのサブタイプのインスタンスのREFを入れることができます。

サブタイプがスーパータイプの単なる特殊な種類である場合は、型が代入可能であることを期待します。ただし、形式上、サブタイプは元々型の1つで、上位のスーパータイプと同じ型ではありません。学生全員や、就業者全員など、個人全員が入っている列には、複数の型のデータが実際に格納されています。

基本的に、オブジェクト属性、コレクション要素およびREFは常に代入可能です。型定義のレベルでは、これらの代入性をあるサブタイプに制限する構文はありません。ただし、特定の表や列について、記憶域レベルの代入性を無効にすることも、制約を付けることもできます。

2.3.12 列および行の代入性

オブジェクト表およびビューの中のオブジェクト型の列と行は代入可能です。つまり、特定の型の列または行にはその型とそのサブタイプのインスタンスを入れることができます。

内容は次のとおりです。

2.3.12.1 列および行の代入性について

オブジェクト表の中のオブジェクト型の列と行は代入できます。

例2-14に示したような型階層person_typについて考えます。すべての型の行が含まれるperson_typのオブジェクト表を作成できます。このためには、例2-20で示したように、特定の型のインスタンスを、INSERT文のVALUES句の中でその型のコンストラクタを使用してオブジェクト表に挿入します。

同様に、例2-23に示すように、リレーショナル表またはビューで型person_typの代入可能列に3つの型すべてのインスタンスを入れることもできます。この例では、その型階層から個人、学生および定時制学生のオブジェクトを再作成して、person_typcontactに挿入します。

例2-23 表の代入可能な列への値の挿入

DROP TYPE person_typ FORCE;
-- if previously created

DROP TYPE student_typ FORCE; -- if previously created

DROP TYPE part_time_student_typ FORCE; -- if previously created
DROP TABLE contacts; if previously created
CREATE OR REPLACE TYPE person_typ AS OBJECT (
  idno           NUMBER,
  name           VARCHAR2(30),
  phone          VARCHAR2(20))
  NOT FINAL;/
CREATE TYPE student_typ UNDER person_typ (
    dept_id NUMBER,
    major VARCHAR2(30))
    NOT FINAL;
/
CREATE TYPE part_time_student_typ UNDER student_typ (
  number_hours NUMBER);
/
CREATE TABLE contacts (
  contact         person_typ,
  contact_date    DATE );

INSERT INTO contacts 
  VALUES (person_typ (12, 'Bob Jones', '650-555-0130'), '24 Jun 2003' );

INSERT INTO contacts 
  VALUES (student_typ(51, 'Joe Lane', '1-650-555-0178', 12, 'HISTORY'),
         '24 Jun 2003' );

INSERT INTO contacts 
  VALUES (part_time_student_typ(52, 'Kim Patel', '1-650-555-0190', 14,
          'PHYSICS', 20), '24 Jun 2003' );

新しく作成したサブタイプは、代入可能な表およびそのスーパータイプの列のいずれにも(サブタイプを作成する前に存在していた表や列など)格納できます。

一般に、属性はドット表記法を使用してアクセスできます。1つの行または列の宣言された型のサブタイプの属性にアクセスするには、TREATファンクションを使用します。次に例を示します。

SELECT TREAT(contact AS student_typ).major FROM contacts;

TREATを参照してください。

2.3.12.2 代入可能な行でのOBJECT_VALUEおよびOBJECT_IDの使用

代入可能な行のオブジェクト識別子(OID)および値にアクセスし、識別できます。

例2-24に示すように、OBJECT_VALUEおよびOBJECT_ID疑似列を使用して、オブジェクト表内の代入可能な行の値およびオブジェクト識別子にアクセスし、識別できるようにします。

関連項目:

これらの擬似列の詳細は、次のマニュアルを参照してください。

例2-24 OBJECT_VALUEおよびOBJECT_IDの使用

DROP TABLE person_obj_table; -- required if previously created
CREATE TABLE person_obj_table OF person_typ;

INSERT INTO person_obj_table
  VALUES (person_typ(20, 'Bob Jones', '650-555-0130'));

SELECT p.object_id, p.object_value FROM person_obj_table p;

2.3.12.3 スーパータイプの属性を持つサブタイプ

サブタイプは、型がスーパータイプの型である属性を持つことが可能です。次に例を示します。

例2-25 スーパータイプの属性を持つサブタイプの作成

-- requires Ex 2-22
CREATE TYPE student_typ UNDER person_typ (
    dept_id   NUMBER,
    major     VARCHAR2(30),
    advisor   person_typ);
/

ただし、このタイプの列は代入可能ではありません。同様に、サブタイプは、要素の型がそのスーパータイプの1つになっているコレクション属性を持つことができますが、この場合もこのタイプの列は代入可能ではありません。たとえば、student_typperson_typのネストした表またはVARRAYがある場合、student_typ列は代入可能ではありません。

ただし、スーパータイプを参照するREF属性を持つサブタイプの代入可能な列が定義できます。たとえば、例2-26に示すcomposite_category_typサブタイプには、ネストした表subcategory_ref_listが含まれています。この表には、category_typのREFであるsubcategory_ref_list_typが含まれています。サブタイプは、次のようにして作成されました。

例2-26 REF属性を持つサブタイプの列の定義

-- not to be executed
CREATE TYPE subcategory_ref_list_typ
  AS TABLE OF REF category_typ; 
/ 

CREATE TYPE composite_category_typ
  UNDER category_typ 
    ( 
      subcategory_ref_list subcategory_ref_list_typ 
...

「新しい表での代入性の無効化」を参照してください。

2.3.12.4 REF列および属性の代入

REF列および属性は、ビューでも表でも代入可能です。たとえば、ビューまたは表のいずれでも、REF person_typとして宣言された列に、person_typまたはそのサブタイプのインスタンスへの参照を入れることができます。

2.3.12.5 コレクション要素の代入

コレクション要素は、ビューでも表でも代入可能です。たとえば、person_typのネストした表には、person_typまたはそのサブタイプのオブジェクト・インスタンスを入れることができます。

2.3.13 代入可能な列に格納される新規作成したサブタイプ

サブタイプを作成すると、スーパータイプの代入可能な列をすでに持つ表に、新しいサブタイプも格納できるようになります。

つまり、サブタイプの作成には、このような表の有無が影響します。このような表が存在する場合、代入可能なサブタイプ(表の制約に違反しないサブタイプ)のみ作成できます。

次の例では、person_typを作成してから、person_typの下にstudent_typサブタイプを作成しようとします。

例2-27 代入可能な列の作成後のサブタイプの作成

DROP TYPE person_typ FORCE;
DROP TABLE person_obj_table;
DROP TYPE student_typ; 
-- perform above drops if objects/tables created
CREATE OR REPLACE TYPE person_typ AS OBJECT (
  idno           NUMBER,
  name           VARCHAR2(30),
  phone          VARCHAR2(20))
  NOT FINAL;/

CREATE TABLE person_obj_table (p person_typ);

ただし、student_typはスーパータイプの属性を持ち、person_obj_table表に、スーパータイプの代入可能な列pがあるため、次の文は正常に実行されません。

CREATE TYPE student_typ UNDER person_typ ( -- incorrect CREATE subtype
    advisor person_typ);
/

次の操作は正常に実行されます。このようなstudent_typサブタイプは、代入可能です。person_obj_table表は、自動的にこの新しい型のインスタンスを格納できるようになります。

CREATE TYPE student_typ UNDER person_typ (
    dept_id NUMBER,
    major VARCHAR2(30));/
INSERT INTO person_obj_table 
  VALUES (student_typ(51, 'Joe Lane', '1-650-555-0178', 12, 'HISTORY'));

2.3.14 代入可能な列の作成後のサブタイプの削除

VALIDATEオプションを使用してサブタイプを削除すると、スーパータイプの代入可能な列のいずれにもサブタイプのインスタンスが格納されていないことが確認されます。このようなインスタンスが存在しない場合は、DROP操作が完了します。

次の文は、person_obj_table表の代入可能な列pstudent_typのインスタンスが格納されているため、正常に実行されません。

DROP TYPE student_typ VALIDATE -- incorrect: an instance still exists ; 

型を削除するには、最初にスーパータイプの代入可能な列にあるそのインスタンスを削除します。

-- Delete from table and drop student_typ subtype example, not sample schema
DELETE FROM person_obj_table WHERE p IS OF (student_typ); 

DROP TYPE student_typ VALIDATE;

関連項目:

DROPおよびVALIDATEの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

2.3.15 新しい表での代入性の無効化

表を作成する際、埋込み属性やネストされたコレクションを含め、列または属性についての代入性はすべて無効にできます。

表を作成する際、NOT SUBSTITUTABLE AT ALL LEVELS句を使用します。

これにより、埋込み属性や任意のレベルにネストされたコレクションを含め、列または属性の代入性はすべて無効になります。

次の例に示す句は、office_typインスタンスのみを格納するように、リレーショナル表のoffice列に制約を設定し、サブタイプのどのインスタンスも使用できなくします。

例2-28 表の作成時の代入性の無効化

DROP TYPE location_typ FORCE; -- required if previously created
DROP TYPE office_typ FORCE; -- required if previously created
CREATE OR REPLACE TYPE location_typ AS OBJECT (
  building_no  NUMBER,
  city         VARCHAR2(40) );
/

CREATE TYPE people_typ AS TABLE OF person_typ;
/
CREATE TYPE office_typ AS OBJECT (
  office_id    VARCHAR(10),
  location     location_typ,
  occupant     person_typ )
  NOT FINAL;/

CREATE TABLE dept_office (
  dept_no      NUMBER,
  office       office_typ)
  COLUMN office NOT SUBSTITUTABLE AT ALL LEVELS;

オブジェクト表の場合は、次のように句を表全体に適用できます。

DROP TABLE office_tab; -- if previously created
CREATE TABLE office_tab OF office_typ
  NOT SUBSTITUTABLE AT ALL LEVELS;

また、この句で特定の列(つまり、表のオブジェクト型の特定の属性)の代入性を無効にすることもできます。

DROP TABLE office_tab; -- if previously created
CREATE TABLE office_tab OF office_typ
  COLUMN occupant NOT SUBSTITUTABLE AT ALL LEVELS;

次のような構文を使用すると、コレクションの要素型が代入性を持たないように指定できます。

DROP TABLE people_tab;
-- required if previously created
CREATE TABLE people_tab (
    people_column people_typ )
    NESTED TABLE people_column 
      NOT SUBSTITUTABLE AT ALL LEVELS STORE AS people_column_nt;

REF列の代入性を無効にするメカニズムはありません。

オブジェクト列に制約を付けるには、NOT SUBSTITUTABLE AT ALL LEVELSIS OF typeのいずれか一方のみが使用可能で、両方は使用できません。

2.3.16 代入性の制約

オブジェクト列または属性で許可されているサブタイプの範囲を、宣言された型の階層内の特定のサブタイプに制限するように、制約を加えることができます。

これを行うには、IS OF typeの制約を使用します。

次の文は、占有者が従業員のみに限定されるoffice_typの表を作成します。

例2-29 表の作成時の代入性の制約

DROP TABLE office_tab;
-- if previously created
CREATE TABLE office_tab OF office_typ
  COLUMN occupant IS OF (ONLY employee_typ);

office_typ型がperson_typ型の占有者を許可している場合でも、列宣言によりemployee_typのインスタンスのみを格納するように制約されます。

行オブジェクトおよび列オブジェクトを(複数ではなく)1つのサブタイプに限定する場合、使用できるのはIS OF typeの演算子のみで、前述の例のようにONLYキーワードを使用する必要があります。

オブジェクト列に制約を付けるには、IS OF typeNOT SUBSTITUTABLE AT ALL LEVELSのいずれか一方のみが使用可能で、両方は使用できません。

2.3.17 表での代入性の変更

既存の表で、ALTER TABLE文を使用して、オブジェクト列をSUBSTITUTABLEからNOT SUBSTITUTABLE(またはNOT SUBSTITUTABLEからSUBSTITUTABLE)に変更できます。

特定の列に対し、[NOT] SUBSTITUTABLE AT ALL LEVELS句をALTER TABLE文に指定します。

代入性は、特定の列に対してのみ変更できます。オブジェクト表全体の代入性は変更できません。

次の文により、列officeが代入可能になります。

例2-30 表内の代入性の変更

-- Requires Ex. 2-28
ALTER TABLE dept_office
  MODIFY COLUMN office SUBSTITUTABLE AT ALL LEVELS;  

次の文により、列が代入不可になります。FORCEキーワードも使用されている点に注意してください。このキーワードは、サブタイプ属性の型ID情報またはデータが含まれている非表示列を削除します。

-- Alter table substitutability with FORCE 
ALTER TABLE  dept_office
  MODIFY COLUMN office NOT SUBSTITUTABLE AT ALL LEVELS FORCE;
--DROP TABLE dept_office;

列を代入不可にする際にFORCEキーワードを使用しない場合、列および型の全属性をFINALにしないと、ALTER TABLE文の実行は失敗します。

VARRAY列は、VARRAY自体の要素型がFINALであり、FINALでない埋込み型を(自らの属性内や埋込み型の属性内などに)所有しない場合のみ、SUBSTITUTABLEからNOT SUBSTITUTABLEに変更できます。

関連項目:

型IDおよびサブタイプ属性の非表示列の詳細は、「代入可能な列およびオブジェクト表の非表示列」を参照してください。

2.3.18 代入性の変更の制限事項

ALTER TABLE文では、一度に1列の代入性のみ変更できます。

複数の列の代入性を変更する場合、複数の文を発行する必要があります。

オブジェクト表では、表の作成時に代入性が表レベルで明示的に設定されていない場合のみ、列の代入性を変更できます。

たとえば、次の例では、CREATE TABLE文により、代入性が明示的に表レベルで有効または無効に設定されていないため、列アドレスの代入性の変更は成功します。

DROP TABLE office_tab;
-- if previously created
CREATE TABLE office_tab OF office_typ;

ALTER TABLE office_tab
  MODIFY COLUMN occupant NOT SUBSTITUTABLE AT ALL LEVELS FORCE;

ただし、次の例では、代入性が表レベルで明示的に設定されているため、列アドレスの設定の変更は失敗します。

DROP TABLE office_tab;
-- if previously created
CREATE TABLE office_tab OF office_typ
  NOT SUBSTITUTABLE AT ALL LEVELS;

/* Following SQL statement generates an error: */
ALTER TABLE office_tab 
  MODIFY COLUMN occupant SUBSTITUTABLE AT ALL LEVELS FORCE  -- incorrect ALTER;

IS OF type演算子によってすでに代入性が制約されている列は、[NOT] SUBSTITUTABLE AT ALL LEVELS句によって代入性を変更することはできません。

関連項目:

IS OF typeの詳細は、代入性の制約を参照してください。

2.3.19 型をまたがる代入

この項で説明する代入規則は、INSERT/UPDATE文、RETURNING句、ファンクションのパラメータ、およびPL/SQL変数に適用されます。

内容は次のとおりです。

2.3.19.1 オブジェクト代入の代表的なオブジェクト

代入性とは、上位のスーパータイプの1つの代理を務めるサブタイプの能力のことです。

サブタイプのかわりにスーパータイプを使用するなど、逆の方向で置換を行うと、コンパイル時にエラーが発生します。

source_typのソースを型target_typのターゲットに代入するパターンは、次のいずれかである必要があります。

  • ケース1: source_typおよびtarget_typが同一型である。

  • ケース2: source_typtarget_typのサブタイプである(ワイドニング)。

ケース2は、ワイドニングを示しています。

2.3.19.2 ワイドニング代入

ワイドニングとは、ターゲットが宣言された型よりも、ソースが宣言された型の方が範囲が特定される代入のことです。

ワイドニングの例として、個人タイプの変数への従業員インスタンスの代入があります。

個人の範囲を絞って定義し、対象を特化したのが従業員であるため、個人をさらに特化して従業員にする存在を無視する場合、個人用のスロットに従業員を入れることができます。すべての従業員が個人のため、ワイドニング代入は常に機能します。

次の表を使用して、ワイドニングについて説明します。

TABLE T(pers_col person_typ, emp_col employee_typ,
stu_col student_typ)

次に示す代入は、ワイドニングを示しています。perscolが代入不可と定義されていないかぎり、代入は有効です。

UPDATE T set pers_col = emp_col;

次の例のPL/SQLでは、最初にperson_typおよびemployee_typを作成する必要があります。

例2-31 PL/SQLの代入

DROP TYPE person_typ FORCE;
-- if previously created
CREATE TYPE person_typ AS OBJECT (
 idno           NUMBER,
 name           VARCHAR2(30),
 phone          VARCHAR2(20))
 NOT FINAL;
/
DROP TYPE employee_typ FORCE; -- if previously created
CREATE TYPE employee_typ UNDER person_typ (
   emp_id NUMBER, 
   mgr VARCHAR2(30));
/
-- PL/SQL assignment example
DECLARE
  var1 person_typ;
  var2 employee_typ;
BEGIN
  var2 := employee_typ(55, 'Jane Smith', '1-650-555-0144', 100, 'Jennifer Nelson');
  var1 := var2;
END;
/

2.3.19.3 ナローイング代入

ナローイング代入はワイドニングの逆です。

ナローイング代入では、個人など一般化した広い範囲を対象とする型を、従業員など範囲を限定して定義された型とみなすことを意味します。すべての個人が従業員であるとはかぎらないため、このような特定の代入は、該当する個人が偶然に従業員の場合のみ機能します。このため最終的には、「オブジェクト代入の代表的なオブジェクト」に示したように、ナローイング代入はケース1のような場合にしか機能しません。

ナローイング代入を行うには、TREATファンクションを使用して、より一般的な宣言型のソース・インスタンスが実際には限定されたターゲット型のインスタンスであり、そのように操作可能であることをテストする必要があります。TREATファンクションは実行時にこのことを確認するためのチェックを行い、ソース値(該当する個人)がターゲット型でもサブタイプの1つでもない場合は、NULLを戻します。

たとえば、次のUPDATE文は、列perscolperson_typの値を、employee_typの列empcolに設定します。perscolのそれぞれの値についての代入が成功するのは、この個人が従業員でもある場合です。個人が従業員ではない場合、TREATNULLを戻し、代入がNULLに戻ります。

UPDATE T set emp_col = TREAT(pers_col AS employee_typ);

次の文は、ソース値の宣言された型を明示的に変更せずに、代入のナローイングを試行します。この文は、エラーを戻す結果になります。

UPDATE T set emp_col = pers_col;

2.3.19.4 コレクションの代入

コレクション型の式の代入では、ソースの宣言された型がターゲットのそれと一致する必要があります。

ワイドニングとナローイングのどちらも、コレクション型の式の代入では使用できません。しかし、スーパータイプのコレクションにサブタイプ値を代入できます。たとえば、新しいstudent_typを作成した後、次のようなコレクション型があるとします。

例2-32 コレクションperson_setの作成

-- Requires 2-21
DROP student_typ;
-- if previously created
CREATE TYPE student_typ UNDER person_typ (
    dept_id NUMBER,
    major VARCHAR2(30))
  NOT FINAL;
/
CREATE TYPE person_set AS TABLE OF person_typ;
/

CREATE TYPE student_set AS TABLE OF student_typ;
/

これらの異なるコレクション型の式を相互に代入することはできませんが、student_typのコレクション要素はperson_set型のコレクションに代入できます。

DECLARE
  var1 person_set; 
  var2 student_set;
  elem1 person_typ; 
  elem2 student_typ;
BEGIN
--  var1 := var2;   /* ILLEGAL - collections not of same type */
  var1 := person_set (elem1, elem2);   /* LEGAL : Element is of subtype */
END;
/