프로퍼티 오버라이드와 재선언

프로퍼티 오버라이드와 재선언

프로퍼티는 하위 클래스에서 오버라이드(override)될 수도 있고 재선언(redeclare)될 수도 있어요.

출처: Overriding and redeclaring properties

본문

프로퍼티 타입이 선언되면 프로퍼티 재선언(redeclaration)으로 동작하고, 그렇지 않으면 프로퍼티 오버라이드(override)예요. 유일한 차이는 이래요. 오버라이드는 상속받은 수식어를 새 수식어로 대체하거나 확장하지만, 재선언은 재선언에 존재하지 않는 상속받은 모든 수식어를 숨겨요. 재선언된 프로퍼티의 타입은 부모 클래스 프로퍼티 타입과 같을 필요는 없어요.

아래 예시가 오버라이드와 재선언의 차이를 보여줘요.


type
 
  TAncestor = class
 
  private
 
    FP1 : Integer;
 
  public
 
    property P: integer Read FP1 write FP1;
 
  end;
 

 
  TP1 = class(TAncestor)
 
  public
 
    // property override
 
    property P default 1;
 
  end;
 

 
  TPReadOnly = class(TAncestor)
 
  public
 
    // property redeclaration
 
    property P: integer Read FP1;
 
  end;

TP1은 프로퍼티 P를 기본값으로 확장하고, TPReadOnly는 프로퍼티 P를 읽기 전용으로 재선언해요.

Remark TP1은 생성자에서 P의 기본값을 1로 설정해야 해요.

프로퍼티 재선언과 오버라이드 모두에서 getter와 setter에 대한 접근은 항상 정적(static)이에요. 즉 프로퍼티 오버라이드는 객체의 RTTI에만 작용하지, 메서드 오버라이드와 혼동하면 안 돼요.

inherited 키워드는 프로퍼티의 부모 정의를 참조하는 데 쓸 수 있어요. 예를 들어 다음 코드를 볼게요.


type
 
  TAncestor = class
 
  private
 
    FP1 : Integer;
 
  public
 
    property P: integer Read FP1 write FP1;
 
  end;
 

 
  TClassA = class(TAncestor)
 
  private
 
    procedure SetP(const AValue: char);
 
    function getP : Char;
 
  public
 
    constructor Create;
 
    property P: char Read GetP write SetP;
 
  end;
 

 
procedure TClassA.SetP(const AValue: char);
 

 
begin
 
  Inherited P:=Ord(AValue);
 
end;
 

 
procedure TClassA.GetP : char;
 

 
begin
 
  Result:=Char((Inherited P) and $FF);
 
end;

TClassA는 P를 정수 프로퍼티 대신 문자 프로퍼티로 재정의하되, 값을 저장하려고 부모의 P 프로퍼티를 사용해요.

프로퍼티에 가상 get/set 루틴을 쓸 때는 주의해야 해요. 상속받은 프로퍼티를 설정하는 것도 여전히 메서드의 일반 상속 규칙을 따른다. 다음 예시를 볼게요.


type
 
  TAncestor = class
 
  private
 
    procedure SetP1(const AValue: integer); virtual;
 
  public
 
    property P: integer write SetP1;
 
  end;
 

 
  TClassA = class(TAncestor)
 
  private
 
    procedure SetP1(const AValue: integer); override;
 
    procedure SetP2(const AValue: char);
 
  public
 
    constructor Create;
 
    property P: char write SetP2;
 
  end;
 

 
constructor TClassA.Create;
 
begin
 
  inherited P:=3;
 
end;

이 경우 상속받은 프로퍼티 P를 설정할 때 SetP1 메서드가 오버라이드됐으므로 TClassA.SetP1 구현이 호출돼요.

부모 클래스의 SetP1 구현을 호출해야 한다면 명시적으로 호출해야 해요.


constructor TClassA.Create;
 
begin
 
  inherited SetP1(3);
 
end;

재선언된 조상 프로퍼티는 조상으로의 직접 캐스트(direct cast)로 하위 객체 안팎에서 접근할 수 있어요.


function GetP(const AClassA: TClassA): Integer;
 
begin
 
  Result := TAncestor(AClassA).P;
 
end;

더 알아보기

  • 프로퍼티 정의(Definition)
  • 저장 정보(Storage information)