継承、メソッドの結合

TBase というクラスがあり、以下のように定義されていたとします。

TBase = class(TObject)
private
  FNumber: Integer;
public
  procedure GetNumber;
  procedure SetNumber(num: Integer);
end;

次に、この TBase クラスから派生した Derived クラスを定義します。

TDerived = class(TBase)
end;

クラス TDerived は、フィールドやメソッドを明示的に定義していませんが、既に上位クラス TBase からフィールド FNumber、メソッド GetNumber, SetNumer を引き継いでいます。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

  TBase = class(TObject)
  private
    FNumber: Integer;
  public
    procedure GetNumer;
    procedure SetNumber(num: Integer);
  end;

  TDerived = class(TBase)
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TBase }

procedure TBase.GetNumer;
begin
  ShowMessage(IntToStr(FNumber));
end;

procedure TBase.SetNumber(num: Integer);
begin
  FNumber := num;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Derived: TDerived;
begin
  Derived := TDerived.Create;
  Derived.SetNumber(123);
  Derived.GetNumer;
  Derived.Free;
end;

end. 

またとても重要なことですが、上位クラス型の変数に下位オブジェクトを代入する事が可能です。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

  TBase = class(TObject)
  private
    FNumber: Integer;
  public
    procedure GetNumer;
    procedure SetNumber(num: Integer);
  end;

  TDerived = class(TBase)
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TBase }

procedure TBase.GetNumer;
begin
  ShowMessage(IntToStr(FNumber));
end;

procedure TBase.SetNumber(num: Integer);
begin
  FNumber := num;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Base: TBase;
begin
  Base := TDerived.Create;
  Base.SetNumber(123);
  Base.GetNumer;
  Base.Free;
end;

end.

このように上位オブジェクト型の変数に下位オブジェクトが代入可能です。最初に言いましたように継承したクラスは、その上位クラスからフィールド、メソッドを引き継ぐと説明しました。それでは継承したクラスの中で、明示的に上位クラスから引き継いだメソッドを再び宣言した場合はどうなるのでしょうか。その例を見てみましょう。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

  TBase = class(TObject)
  public
    procedure ShowClassName;
  end;

  TDerived = class(TBase)
  public
    // ShowClassName は TBase クラスから継承されていますが、
    // 継承先のクラスで再び宣言をしてみます。
    procedure ShowClassName;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TBase }

procedure TBase.ShowClassName;
begin
  ShowMessage('Base');
end;

{ TDerived }

procedure TDerived.ShowClassName;
begin
  ShowMessage('Derived');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Base: TBase;
  Derived: TDerived;
begin
  Base := TBase.Create;
  Base.ShowClassName; // TBase クラスの ShowClassName が呼ばれる
  Base.Free;

  Derived := TDerived.Create;
  Derived.ShowClassName; // TDerived クラスの ShowClassName が呼ばれる
  Derived.Free;

  Base := TDerived.Create;
  Base.ShowClassName; // どっちの ShowClassName が呼ばれるのか?
  Base.Free;
end;

end.

上位クラス型の変数に下位オブジェクトを代入し、メソッド ShowClassName を呼び出したところ「Base」と表示されました。このように継承されているメソッドを下位クラスで明示的に宣言した場合には、どのような仕組みでメソッドの呼び出しが行われるのかを知る必要があります。

メソッドの結合

クラスのメソッドは、静的(これがデフォルトです。非仮想とも呼ばれます)、仮想、動的として宣言する事ができます。静的メソッドの場合、そのメソッドの呼び出しで使用された変数の宣言時の型によって、どのメソッドが呼び出されるかが決定されます。先ほどの例をもう一度詳しく見てみます。

  TBase = class(TObject)
  public
    procedure ShowClassName; // 静的メソッド
  end;

  TDerived = class(TBase)
  public
    procedure ShowClassName; // 静的メソッド
  end;
..................................

procedure TForm1.Button1Click(Sender: TObject);
var
  // Base は TBase 型の変数として宣言
  Base: TBase;
  // Derived は TDerived 型の変数として宣言
  Derived: TDerived;
begin
  
  Base := TBase.Create;
  
  // Base 変数からメソッド ShowClassName が呼ばれる
  // ShowClassName は静的メソッドであるので、
  // TBase クラスで定義してある ShowClassName が呼ばれる 
  Base.ShowClassName; 
  Base.Free;


  Derived := TDerived.Create;
  
  // Derived 変数からメソッド ShowClassName が呼ばれる
  // ShowClassName は静的メソッドであるので、
  // TDerived クラスで定義してある ShowClassName が呼ばれる 
  Derived.ShowClassName;
  Derived.Free;


  Base := TDerived.Create;
  
  // Base 変数からメソッド ShowClassName が呼ばれる
  // ShowClassName は静的メソッドであるので、
  // TBase クラスで定義してある ShowClassName が呼ばれる 
  // なぜなら Base 変数は、TBase 型の変数として宣言されているので。
  Base.ShowClassName;
  Base.Free;
end;

end.

このように静的メソッドが呼ばれる場合には、そのメソッド呼び出しの際に使用される変数の宣言時の型で、どのメソッドが呼ばれるのかが決定されています。

仮想メソッド、動的メソッド

仮想メソッド、動的メソッドを宣言する場合には、その宣言時にそれぞれキーワード virtual, dynamic を付けてやります。この仮想メソッド、動的メソッドは意味的には2つとも等価ですので、ここでは仮想メソッドについてだけ取り上げていきます。

仮想メソッドを指定する事で静的メソッドと何に違いがあるのかというと、それは上位クラス型変数に、下位オブジェクトを代入した場合のメソッド呼び出し時にあります。仮想メソッドや動的メソッドは下位クラスでオーバーライドする事が出来ます。そしてオーバーライドされたメソッドが呼び出される際には、メソッド呼び出し時に使用される変数の宣言時の型ではなく実際に代入されたオブジェクトの型によって、どのメソッドが呼ばれるかが決定します。(オーバーライドとは、上位クラスから継承されたメソッドを下位クラスで再定義することを指します。これは、上位クラスでの実装を下位クラスでまるごと置き換えることを意味しているので、どのクラスのメソッドを呼び出すかにより動作をまるっきり変える事が可能となります!)メソッドをオーバーライドするには、キーワード override を使用してメソッドを再宣言します。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

  TBase = class(TObject)
  public
    procedure ShowClassName; virtual; // 仮想メソッド
  end;

  TDerived = class(TBase)
  public
    procedure ShowClassName; override; // オーバーライド
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


{ TBase }

procedure TBase.ShowClassName;
begin
  ShowMessage('Base');
end;

{ TDerived }

procedure TDerived.ShowClassName;
begin
  ShowMessage('Derived');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Base: TBase;
  Derived: TDerived;
begin
  // TBase 型の変数 Base に TBase 型のオブジェクトを代入
  Base := TBase.Create;
  
  // 呼び出されるメソッドは、Base に代入されているオブジェクトの型のメソッド
  // よって、TBase 型で宣言されている ShowClassName が呼ばれる
  Base.ShowClassName;
  Base.Free;


 // TDerived 型の変数 Derived に TDerived 型のオブジェクトを代入
  Derived := TDerived.Create;
  
  // 呼び出されるメソッドは、Derived に代入されているオブジェクトの型のメソッド
  // よって、TDerived 型で宣言されている ShowClassName が呼ばれる
  Derived.ShowClassName;
  Derived.Free;


 // TBase 型の変数 Base に TDerived 型のオブジェクトを代入
  Base := TDerived.Create;
  
  // 呼び出されるメソッドは、Base に代入されているオブジェクトの型のメソッド
  // よって、TDerived 型で宣言されている ShowClassName が呼ばれる
  Base.ShowClassName;
  Base.Free;
end;

end.

注意しなければならないのは、オーバーライドできるのは仮想メソッド、動的メソッドだけです。静的メソッドはオーバーライド出来ません。


up next
Last update: 2002/8/26