コンストラクタの呼び出しには、クラス参照型の変数を用いることが出来ます。 クラス参照型変数を用いてコンストラクタを呼び出すと、コンパイル時にその型がわからないインスタンスを生成することが可能となります。 ここでは実際に、このクラス参照型のパラメータをもつメソッドを作成して、 インスタンス生成をみてみましょう。
サンプルプログラムは、以下のようになります。
Unit2 について
unit Unit2; interface uses SysUtils, StrUtils; type TBase = class(TObject) private fStr: string; protected function Process: string; virtual; abstract; public constructor Create(str: string); procedure Print; property Str: string read fStr; end; TDefault = class(TBase) protected function Process: string; override; end; TUpper = class(TBase) protected function Process: string; override; end; TReverse = class(TBase) protected function Process: string; override; end; TBaseClass = class of TBase; TFactory = class(TObject) public class function CreateStr(BaseClass: TBaseClass; str: string): TBase; end; implementation uses Unit1; { TBase } constructor TBase.Create(str: string); begin fStr := str end; procedure TBase.Print; begin Form1.Memo1.Lines.Add(Process); end; { TDefault } function TDefault.Process: string; begin Result := Str; end; { TUpper } function TUpper.Process: string; begin Result := UpperCase(Str); end; { TReverse } function TReverse.Process: string; begin Result := ReverseString(Str); end; { TNewFactory } class function TFactory.CreateStr(BaseClass: TBaseClass; str: string): TBase; begin Result := BaseClass.Create(str); end; end.
Unit1 について
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; Button4: TButton; Button5: TButton; Button6: TButton; procedure Button4Click(Sender: TObject); procedure Button5Click(Sender: TObject); procedure Button6Click(Sender: TObject); private { Private 宣言 } public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} uses Unit2; procedure TForm1.Button4Click(Sender: TObject); var Default: TBase; begin Default := TFactory.CreateStr(TDefault, 'Hello'); Default.Print; Default.Free; end; procedure TForm1.Button5Click(Sender: TObject); var Reverse: TBase; begin Reverse := TFactory.CreateStr(TReverse, 'Hello'); Reverse.Print; Reverse.Free; end; procedure TForm1.Button6Click(Sender: TObject); var Upper: TBase; begin Upper := TFactory.CreateStr(TUpper, 'Hello'); Upper.Print; Upper.Free; end; end.
コンパイルし、実行しますと図のように引数として渡された文字列"Hello"が 実際に生成されるインスタンスに応じて、異なる処理が行われ、 Memo に表示 されています。
Unit2 について見ていきましょう。
TBase = class(TObject) private fStr: string; protected function Process: string; virtual; abstract; public constructor Create(str: string); procedure Print; property Str: string read fStr; end; TDefault = class(TBase) protected function Process: string; override; end; TUpper = class(TBase) protected function Process: string; override; end; TReverse = class(TBase) protected function Process: string; override; end;
まず、TBase クラスでコンストラクタと 2 つのメソッド(Print, Process)が 定義されています。Process メソッドは、仮想メソッドであり、且つ抽象メソッドとして定義されています。つまり、このメソッドは TBase クラスの サブクラス(子クラス)に実装がまかされていることになります。 また、Process メソッドのアクセス制御を protected にしたのは、 このクラスと、このクラスから派生するサブクラスだけが、このメソッドを利用できるようにするためです(厳密には、protected で 定義されるメソッドは、サブクラスが定義されているユニットのコードからもアクセスできます)。
TBase クラスのサブクラスである TDefault, TUpper, TReverse クラスを 見ていきましょう。それぞれのクラスでは Process メソッドを次のように実装しています。
{ TDefault } function TDefault.Process: string; begin Result := Str; end; { TUpper } function TUpper.Process: string; begin Result := UpperCase(Str); end; { TReverse } function TReverse.Process: string; begin Result := ReverseString(Str); end;
TDefault クラスでの Process メソッドでは、プロパティ Str に何も処理を施さず、そのまま呼び出し元に 返しています。TUpper クラスでの Process メソッドは、Str を UpperCase 関数に渡して、全ての文字を 大文字にしてから、呼び出し元に返しています。TReverse クラスでは、ReverseString 関数で、文字列を逆にしてから呼び出し元に返しています。
TBaseClass = class of TBase;
上記のコードは、クラス参照型 TBaseClass を定義しています。 クラス参照とは、単にクラス型の参照です。つまり、TBaseClass クラス参照型の変数が TBase とそのサブクラスを参照できるということです。
TFactory = class(TObject) public class function CreateStr(BaseClass: TBaseClass; str: string): TBase; end;
上記のコードでは TFactory クラスを定義し、クラスメソッド CreateStr を 定義しています。このようにクラスメソッドを定義する場合には、予約語 class を先頭につけます。
クラスメソッドとは、オブジェクトに適用するメソッドではなく、 クラスを操作するメソッドです。ですから、CreateStr クラスメソッドを 呼び出す際に、TFactory をインスタンス化する必要はありません(してはいけない ということではありません)。 また、クラスメソッドは、クラスのフィールドにはアクセスできないことに注意してください。つまり、クラスメソッドは特定のインスタンスに適用されるのではなく、 クラス全体に適用されるメソッドということになります。
class function TFactory.CreateStr(BaseClass: TBaseClass; str: string): TBase; begin Result := BaseClass.Create(str); end;
CreteStr クラスメソッドの内容をみてみますと、クラス参照を用いて コンストラクタを呼び出しています。
それでは、これらのクラスを使用したコードを見てみましょう。
procedure TForm1.Button4Click(Sender: TObject); var Default: TBase; begin Default := TFactory.CreateStr(TDefault, 'Hello'); Default.Print; Default.Free; end; procedure TForm1.Button5Click(Sender: TObject); var Reverse: TBase; begin Reverse := TFactory.CreateStr(TReverse, 'Hello'); Reverse.Print; Reverse.Free; end; procedure TForm1.Button6Click(Sender: TObject); var Upper: TBase; begin Upper := TFactory.CreateStr(TUpper, 'Hello'); Upper.Print; Upper.Free; end;
TFactory のクラスメソッド CreateStr にインスタンス化するクラス型と 文字列を渡しています。クラスメソッド CreateStr では、渡されたクラス型の インスタンスを生成し、その参照を返します。
そして、Print メソッドを呼び出し、Free メソッドでインスタンスの 破棄を実行しています。
クラス参照型については、Delphi で次のような定義を見る事が出来ます。
TClass = class of TObject;
Delphi 6 では System.pas で上記のコードが定義されています。この TClass は、 TObject 型を参照できるということですので、Delphi で定義される クラスは、全て参照できるということになります。
この TClass を用いたサンプルを見てみましょう。
procedure TForm1.Button1Click(Sender: TObject); var aClass: TClass; begin ListBox1.Items.Add(ClassName); aClass := ClassParent; while aClass <> nil do begin ListBox1.Items.Add(aClass.ClassName); aClass := aClass.ClassParent; end; end;
このプログラムは、最初に自分のクラス名(TForm1)を 表示してから、その親クラス(スーパークラス)名を 表示し、さらにその親クラスの親クラスへと順にクラス名を表示させるプログラムです。
最後は、TObject と終了しているのが分かると思います。 これは、TForm1 クラスに限らずどんなクラスでも継承をさかのぼると、最後には TObject にたどり着きます。 つまり、TObject が全てのクラスの親クラス(スーパークラス)ということになります。