@[TOC]
第七章 接口
type
接口名称 = Interface(父接口的名称)//一般是没有的
[GUID]//Ctrl+Alt+G自动生成,是唯一的,如果相同,则表示这两个接口完全一致
//成员列表
end;
注:Interface不能声明为局部类型;
GUID虽然可以省略,但是建议不要省略;
接口的成员只能是方法和属性,属性也只能通过方法读写;
接口中没必要也不允许指定任何访问权限;
声明属性时不能指定stored,default,nodefault,但数组属性可以指定default;
不存在接口类型的对象所以接口中不能含有构造和析构函数;
接口不能实例化,其中的方法不存在动态或静态绑定,所以方法后不能加关键词:
virtual,dynamic,abstract,override;
一个类可以继承多个接口,逗号隔开;
类继承接口时,父类名称不能省略,要写在第一位;
类继承一个接口就要实现这个接口所有的方法和属性;
接口实现举例
type
I1 = Interface
procedure F1;
end;
I2 = Interface
procedure F2;
end;
T2 = class(TObject, I1, I2)
procedure F1;
procedure F2;
end;
所有接口继承自根接口IInterface,其中最基本的方法由类TInterfacedObject实现,在System.pas;
TInterfacedObject = class(TObject, IInterface)
...
end;
当我们调用接口时,可以继承这个类而非TObject作为父类,可以省掉基本方法的实现;
实现接口中的属性举例
type
I1 = Interface
procedure SetX(value: Integer);
function GetX: Integer;
property P1: Integer read GetX write SetX;
end;
T1 = class(TInterfacedObject, I1);
strict private
I: Integer;
procedure SetX(value: Integer);
function GetX: Integer;
published
property P1: Integer read GetX write SetX;
end;
function T1.GetX: Integer;
begin
Result := self.I;
end;
procedure T1.SetX(value: Integer);
begin
Self.I := value;
end;
var
obj: T1;
begin
obj := T1.Create;
obj.P1 := 78;
Writeln(obj.P1);
FreeAndNil(obj);
Readln;
end.
I用于存储P1的值;
在子类中改变父类中实现的接口举例
type
I1 = Interface
procedure F1;
T1 = class(TInterfacedObject, I1);
procedure F1;
end;
T1的派生类T2需要以一种不同的方式来重载实现I1,只需要让T2继承I1即可;
T2 = class(T1, I1)
procedure F1;
end;
procedure T1.F1;
begin
Writeln('T1.F1');
end;
procedure T2.F1;
begin
Writeln('T2.F1');
end;
var
Interface1: I1;
begin
Interface1 := T1.Create;
Interface1.F1;
Interface1 := T2.Create;
Interface1.F1;
end.
注:T2中的I1接口完全隐藏了T1中的I1接口,如果在T1中给I1接口的方法声明了别名,在T2中将失效;
7.4方法别名
主要是解决方法名称相同的问题;
举例
type
I1 = Interface
procedure SetX(value: Integer);
function GetX: Integer;
property P1: Integer read GetX write SetX;
end;
I2 = Interface
procedure SetX(value: Integer);
function GetX: Integer;
property P2: Integer read GetX write SetX;
end;
T1 = class(TInterfacedObject, I1, I2)
strict private
I, J: Integer;
procedure I1.SetX = set1; //other name
procedure I1.GetX = get1;
procedure I2.SetX = set2;
procedure I2.GetX = get2;
public
procedure Set1(value: Integer); //change
function Get1: Integer;
procedure Set2(value: Integer);
function Get2: Integer;
property p1: Integer read Get1 write Set1; //change
property p2: Integer read Get2 write Set2;
end;
别名只是方法的另一个名字,并不能改变方法除了名字之外的任何信息;
7.5接口的代理
如果在T1中继承I1接口,而T0中已经实现了I1接口,可以通过以下方式直接使用
type
I1 = Interface
procedure F1;
end;
T0 = class(TInterfacedObject, I1)
procedure F1;
end;
T1 = class(TInterfacedObject, I1)
strict private
FInterface: T1;
public
property P1: I1 read FInterface write FInterface Implements I1;
end;
procedure T0.F1;
begin
Writeln('this is T0.F1');
end.
var
Interface1: I1;
obj: T1;
begin
obj := T1.Create;
obj.P1 := T0.Create;
Interface1 := obj;
Interface1.F1;
Readln;
end.
步骤:
1.在类中声明接口类型的属性;
2.把其他实现了接口的对象赋给这个属性;
3.通过属性中存储的对象调用这个接口的功能;
以上是通过接口作为属性的类型,还可以使用类类型的属性;
T1 = class(TInterfacedObject, I1)
strict private
FInterface: T0; //change
public
//change
property P1: T0 read FInterface write FInterface Implements I1;
end;
procedure T0.F1;
begin
Writeln('T0.F1');
end;
var
Interface1: I1;
obj: T1;
begin
obj := T1.Create;
obj.P1 := T0.Create;
Interface1 := obj;
Interface1.F1;
Readln;
end.
这种方式成为对象代理;
7.6接口的赋值和转型
非接口变量中只有变体变量可以接受接口类型的值;
变体变量第三章;
将一个IDispatch类型的接口值赋给变体变量时,变体变量的类型码的值是varDispatch;
其他任何接口值赋给变体变量时,类型码均为varUnknown;
非接口变量值赋给接口变量:变体变量值赋给接口变量,nil赋给接口,类的对象赋给接口;
当变体变量的类型码是varUnknown时,它可以当成值赋给IInterface(IUnknown)
类型的变量;
类型码是varDispatch或varEmpty时,既可以赋给IInterface(IUnknown)
类型的接口变量,也可以赋给IDispatch类型的接口变量;
其他任何类型都不能赋给接口类型,任何类型也不能接口变体值;
nil可以作为值赋给任何接口类型的变量;
如果将对象赋给某个接口类型的变量,则此对象必须实现了此接口本身,即使是实现了此接口的子代接口也不行;
举例
type
I1 = Interface(IInterface)
End;
I2 = Interface(I1)
End;
T1 = class(TInterfacedObject, I2)
end;
var
obj: T1;
Interface1: I1;
Interface2: I2;
begin
obj := T1.Create;
Interface1 := obj;
//that is wrong
Interface2 := obj;
FreeAndNil(obj);
end.
obj实现了I2接口,它不能作为值赋给I2之外的任何接口类型的变量;
派生的接口值赋给祖先接口变量;
也只有这种情况可以将接口变量值赋给接口变量;
不过这基本没有什么意义,举例
type
I1 = Interface
procedure M1;
end;
I2 = Interface(I1)
procedure M2;
end;
T1 = class(TInterfacedObject, I2)
procedure M1;
procedure M2;
end;
var
obj: T1;
Interface1: I1;
Interface2: I2;
begin
obj := T1.Create;
Interface2 := obj;
Interface2.M2;
Interface1 := Interface2;//here
Interface1.M1;
Readln;
end.
这里相当于
Interface1 := obj;
type
I1 = Interface
end;
T1 = class(TInterfacedObject, I1)
end;
var
obj: T1;
Interface1: I1;
然后可以
Interface1 := obj;
obj := T1(Interface1);
这种转换是一种隐匿转换,某些时候不能确保安全性;
应当使用as操作符;
举例
type
T1 = Interface
['{D8C36ABA-FCF9-46F1-A55B-8E69EEA75244}']
procedure M1;
end;
T1 = class(TInterfacedObject, I1)
procedure M1;
procedure M2;
procedure M3;virtual;
end;
procedure T1.M1;
begin
Writeln('T1.M1');
end;
procedure T1.M2;
begin
Writeln('T1.M2');
end;
procedure T1.M3;
begin
Writeln('T1.M3');
end;
var
Interface1: T1;
obj: T1;
begin
obj := T1.Create;
Interface1 := obj as T1;
Interface1.M1;
Readln;
end.
注:使用as转型必须加上GUID,否则无法编译;
begin
obj := T1.Create;
Interface1 := obj as T1;
// three more code
obj := nil;
obj := Interface1 as T1;
obj.M3;
//end more
Interface1.M1;
Readln;
end.
首先将obj设置成nil,将Interface1转为T1类型的引用赋给obj,调用M3;
注意到M3是虚方法,这就解除了M3和obj之间的静态绑定;
还记得静态绑定吗
接口的生存期受系统自动管理,无需手动销毁;
通过接口实现多态举例
unit Unit3;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm3 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
IGreed = Interface
procedure Greed;
End;
TCHGreen = class(TInterfacedObject, IGreed)
procedure Greed;
end;
TJPGreen = class(TInterfacedObject, IGreed)
procedure Greed;
end;
TENGreen = class(TInterfacedObject, IGreed)
procedure Greed;
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
procedure TCHGreen.Greed;
begin
ShowMessage('zao');
end;
procedure TJPGreen.Greed;
begin
ShowMessage('shang');
end;
procedure TENGreen.Greed;
begin
ShowMessage('hao');
end;
procedure TForm3.Button1Click(Sender: TObject);
var
obj: IGreed;
begin
obj := TCHGreen.Create;
obj.Greed;
end;
procedure TForm3.Button2Click(Sender: TObject);
var
obj: IGreed;
begin
obj := TJPGreen.Create;
obj.Greed;
end;
procedure TForm3.Button3Click(Sender: TObject);
var
obj: IGreed;
begin
obj := TENGreen.Create;
obj.Greed;
end;
end.
这里各个类没有继承TGreed而是实现了IGreed接口;
调用方法通过接口调用;
所有的对象都无需手动销毁;
由于接口的生存周期由系统自动管理,所以它指向的实体也会由系统自动销毁,无需手动销毁。