Thừa kế (hay kế thừa - inheritance) là một trong những tính chất đặc trưng và quan trọng trong lập trình hướng đối tượng.
Kế thừa dựa trên nguyên tắt một class có thể thừa hưởng tất cả các thành viên (trường, phương thức, thuộc tính) từ một class khác. Class thừa kế gọi là subclass (lớp con), class cho được gọi là parentclass (lớp cha) hay baseclass (lớp cơ sở).
Từ những trường, phương thức, thuộc tính có sẵn, lớp con có thể sửa đổi cách thức hoạt động của những phương thức của lớp cha bằng override (ghi đè) và overload (nạp chồng) để phù hợp hơn.
Ở đây chúng ta sẽ lấy một ví dụ đơn giản về thừa kế.
type
TNguoi = class
private
FTuoi: byte;
FName: string;
public
procedure AnUong;
end;
THocSinh = class(TNguoi)
public
procedure HocTap;
end;
Như trên chúng ta khai báo hai lớp, lớp cha TNguoi (người) và lớp con THocSinh (học sinh) vì học sinh cũng là người nên được quyền kế thừa tất cả từ người.
Bên cạnh đó, lớp THocSinh còn sáng tạo thêm một phương thức HocTap, cái đó là của riêng lớp con THocSinh mà lớp cha là TNguoi không có.
Từ khóa Is: Dùng để kiểm tra một đối tượng có phải là kế thừa từ một lớp hay không.
Ví dụ là đoạn mã trên và thêm đoạn bên dưới
var
NguoiA: TNguoi;
HS1: THocSinh;
begin
NguoiA := TNguoi.Create;
HS1 := THocSinh.Create;
if NguoiA is TNguoi then
WriteLn('Nguoi A la nguoi')
else
WriteLn('Nguoi A khong phai la nguoi');
if HS1 is TNguoi then
WriteLn('Hoc sinh 1 la nguoi')
else
WriteLn('Hoc sinh 1 khong phai la nguoi');
if NguoiA is THocSinh then
WriteLn('Nguoi A la hoc sinh')
else
WriteLn('Nguoi A khong phai la hoc sinh');
end.
Kết quả chạy chương trình trên
Nguoi A la nguoi
Hoc sinh 1 la nguoi
Nguoi A khong phai la hoc sinh
Như trên cho thấy, một đối tượng tạo ra từ lớp là chính nó (NguoiA is TNguoi), hoặc đối tượng từ lớp con cũng là của lớp cha (HS1 is TNguoi). Nhưng lớp cha không thể là lớp con (NguoiA is THocSinh).
Từ khóa As
Như khai báo hai lớp TNguoi và THocSinh ở trên, rõ ràng lớp THocSinh có phương thức HocTap mà lớp TNguoi không có. Giả sử, ta có một người đã được xác định là học sinh, nhưng không thể sử dụng được phương thức HocTap vì đã khai báo là TNguoi. Từ khóa as sẽ phát huy công dụng của nó.
var
NguoiA: TNguoi;
begin
NguoiA := THocSinh.Create; // Là học sinh
if NguoiA is THocSinh then
(NguoiA as THocSinh).HocTap;
end.
Từ khóa as xem NguoiA như là một học sinh, từ đó có thể sử dụng được method HocTap.
Lưu ý: Nên sử dụng is để kiểm tra kiểu cho phù hợp, nếu không sẽ bị báo lỗi khi sử dụng as.
Overload (nạp chồng) là cho phép hai phương thức có cùng tên nhưng khác tham số giữa lớp cha và lớp con. Override (ghi đè) tương tự như overload nhưng tham số giống nhau.
Ví dụ về overload giữa một class
type
TCalculator = class
class function Sum(Num1, Num2: Integer): Integer; overload;
class function Sum(Str1, Str2: string): string; overload;
end;
class function TCalculator.Sum(Num1, Num2: Integer): Integer;
begin
Result := Num1 + Num2;
end;
class function TCalculator.Sum(Str1, Str2: string): string;
begin
Result := Str1 + Str2;
end;
...
WriteLn(TCalculator.Sum(1, 2)); // In ra 3
WriteLn(TCalculator.Sum('Viet ', 'Nam')); // In ra Viet Nam
Tôi sử dụng class procedure (xem thêm ở bài trước) để gọi trực tiếp hàm qua class, không cần khởi tạo object bằng Create.
Ở ví dụ trên, tôi khai báo một lớp TCalculator chứa các hàm tính tổng. Có hai hàm tính tổng có cùng tên Sum nhưng tham số khác (Integer với String). Do đó, ta dùng từ khóa overload đặt vào cuối phần khai báo mỗi hàm (chỉ ở phần khai báo, phần triển khai bên dưới không cần thiết).
Mỗi hàm có cách xử lý khác nhau. Nhưng khi ta gọi TCalculator.Sum thì tùy vào tham số, ví dụ tham số là số nguyên thì hàm Sum thứ nhất được gọi, nếu tham số là chuỗi thì hàm Sum thứ 2 được gọi. Như thế gọi là overload.
Thêm một ví dụ về override giữa các class cha và con.
type
TCha = class
class procedure Hello;
end;
TCon = class(TCha)
class procedure Hello; override;
end;
class procedure TCha.Hello;
begin
WriteLn('Hello, my name is Daddy');
end;
class procedure TCon.Hello;
begin
WriteLn('Hi, my name is Son');
end;
...
Ở ví dụ này, chúng ta sử dụng override để ghi đè phương thức Hello của lớp TCha bằng một phương thức Hello mới cho class TCon. Khi ta gọi Hello của TCha, thì thủ tục TCha.Hello được chạy. Ngược lại, nếu lớp con TCon gọi phương thức Hello, thì phương thức TCon.Hello được chạy. Nếu không có từ khóa override ở lớp TCon, thì Hello của TCha sẽ được gọi (bởi vì lớp TCon thừa kế từ TCha).
Từ khóa này giúp gọi đến phương thức có cùng tên trên lớp cơ sở (base class) nếu được sử dụng riêng rẽ như một câu lệnh, hoặc gọi đến phương thức tùy chỉnh trên lớp cơ sở khi đi theo sau là một tên phương thức.
Cũng lấy ví dụ về cha con ở trên, nhưng khi đứa con giới thiệu xong thì nó sẽ chuyển lời giới thiệu sang cho cha.
type
TCha = class
class procedure Hello;
end;
TCon = class(TCha)
class procedure Hello; override;
end;
class procedure TCha.Hello;
begin
WriteLn('Hello, my name is Daddy');
end;
class procedure TCon.Hello;
begin
WriteLn('Hi, my name is Son');
WriteLn('This is my dad !');
inherited Hello; // Dòng này sẽ gọi đến lớp TCha.Hello
end;
...
Phía sau từ khóa inherited là một phương thức. Điều đó có nghĩa là lời gọi đến phương thức đó nhưng ở lớp cha của lớp hiện tại.
Có thể sử dụng từ khóa inherited mà không có gì phía sau. Nó báo hiệu tất cả các trường, phương thức sau này sẽ là của lớp cha hết.
Delphi không hỗ trợ đa kế thừa như C#