Tính Kế Thừa (Inheritance)
"Trong lập trình hướng đối tượng, tính kế thừa (Inheritance) là một nguyên lý quan trọng cho phép một lớp kế thừa các thuộc tính và phương thức từ một lớp khác. Điều này không chỉ giúp tái sử dụng mã nguồn mà còn làm cho việc mở rộng và bảo trì chương trình trở nên dễ dàng hơn. Dưới đây là những điều cơ bản bạn cần biết về tính kế thừa trong Java."
Ⅰ. Kế thừa (Inheritance) là gì
Kế thừa trong java là sự liên quan giữa hai class với nhau, trong đó có class cha (superclass) và class con (subclass). Khi kế thừa class con được hưởng tất cả các phương thức và thuộc tính của class cha. Tuy nhiên, nó chỉ được truy cập các thành viên public và protected của class cha. Nó không được phép truy cập đến thành viên private của class cha.
Cách truy cập thành viên private của lớp cha (một cách gián tiếp)
↳ Mặc dù không thể truy cập trực tiếp, nhưng chúng ta có thể truy cập và thay đổi giá trị của thành viên private của lớp cha một cách gián tiếp thông qua các phương thức public hoặc protected mà lớp cha cung cấp.
↳ Sử dụng phương thức getter và setter: Nếu lớp cha cung cấp các phương thức getter và setter cho thành viên private, lớp con có thể sử dụng các phương thức này để đọc và ghi giá trị của thành viên đó.
↳ Sử dụng phương thức protected: Nếu một thành viên được khai báo là protected, nó có thể được truy cập từ trong lớp con hoặc từ các lớp trong cùng một package.
↳ Ý tưởng đằng sau tính kế thừa trong Java là bạn có thể tạo các lớp mới được xây dựng dựa trên các lớp hiện có. Khi kế thừa từ một lớp hiện có, bạn có thể sử dụng lại các phương thức và trường thuộc tính của lớp cha. Hơn nữa, bạn cũng có thể thêm các phương thức và trường thuộc tính mới trong lớp hiện tại của mình.
↳ Kế thừa đại diện cho mối quan hệ IS-A, còn được gọi là mối quan hệ cha-con.
Tại sao sử dụng kế thừa?
↳ Tái sử dụng mã (Code Reusability): Kế thừa giúp bạn tận dụng lại các thuộc tính và phương thức đã được định nghĩa trong lớp cha. Điều này giúp tiết kiệm thời gian và công sức khi xây dựng các lớp mới.
↳ Tạo mối quan hệ giữa các lớp: Mô hình hóa các mối quan hệ "Cha - Con" (IS-A) trong thế giới thực.
↳ Tính đa hình (Polymorphism): Kế thừa là nền tảng cho tính đa hình, cho phép các đối tượng của các lớp con phản hồi cùng một phương thức theo cách riêng của chúng.
Các thuật ngữ trong kế thừa
↳ Lớp cha (Superclass hoặc Parentclass): Lớp mà các thuộc tính và phương thức được kế thừa.
↳ Lớp con (Subclass hoặc Childclass): Lớp kế thừa các thuộc tính và phương thức từ lớp cha.
↳ Từ khóa extends: Trong Java, từ khóa extends được sử dụng để chỉ rõ một lớp kế thừa từ lớp khác.
↳ Phương thức ghi đè (Overriding): Lớp con định nghĩa lại một phương thức đã có trong lớp cha.
↳ Phương thức nạp chồng (Overloading): Cùng một tên phương thức nhưng với các danh sách tham số khác nhau.
Cú pháp của kế thừa trong Java
Cú pháp để thực hiện kế thừa rất đơn giản:
Cú Pháp
class LớpCon extends LớpCha {
// Các thuộc tính và phương thức của lớp con
}
Trong đó:
↳ LớpCon: Tên của lớp con.
↳ extends: Từ khóa chỉ ra rằng lớp con đang kế thừa từ lớp cha.
↳ LớpCha: Tên của lớp cha.
Các kiểu kế thừa trong java
Chúng ta có thể phân loại kế thừa trong Java như sau:
1. Các kiểu kế thừa theo lớp
↳ Đơn kế thừa (Single Inheritance)
↳ Kế thừa đa cấp (Multilevel Inheritance)
↳ Kế thừa phân cấp (Hierarchical Inheritance)
2. Các kiểu kế thừa theo interface
↳ Đa kế thừa (Multiple Inheritance)
↳ Kế thừa hỗn hợp (Hybrid Inheritance)

Chúng ta sẽ thảo luận về các kiểu kế thừa theo lớp trong phần này. Các kiểu kế thừa thông qua interface sẽ được trình bày chi tiết trong phần về interface.
Ⅱ. Đơn kế thừa (Single Inheritance)
↳ Định nghĩa: Một lớp chỉ có thể kế thừa từ một lớp cha duy nhất. Đây là kiểu kế thừa phổ biến nhất trong Java.

Ví dụ: Example.java
// Lớp cha Hinh
class Hinh {
String Mau;
// Constructor của lớp Hinh
public Hinh(String Mau) {
this.Mau = Mau;
}
// Phương thức để hiển thị Màu của hình
public void display() {
System.out.println("Màu: " + Mau);
}
}
// Lớp con HinhTron kế thừa từ lớp Hinh
class HinhTron extends Hinh {
double BanKinh;
// Constructor của lớp HinhTron
public HinhTron(String Mau, double BanKinh) {
super(Mau); // Gọi constructor của lớp cha
this.BanKinh = BanKinh;
}
// Phương thức để tính diện tích của hình tròn
public double getArea() {
return Math.PI * BanKinh * BanKinh;
}
// Phương thức để hiển thị thông tin của hình tròn
@Override
public void display() {
super.display(); // Gọi phương thức display của lớp cha
System.out.println("Bán Kính Hình Tròn: " + BanKinh);
System.out.println("Diện Tích Hình Tròn: " + getArea());
}
}
// Lớp chính để kiểm tra
public class Example {
public static void main(String[] args) {
// Tạo đối tượng HinhTron
HinhTron hinhtron = new HinhTron("Đỏ", 5.0);
// Hiển thị thông tin của hình tròn
hinhtron.display();
}
}
Kết quả của chương trình là:
Bán Kính Hình Tròn: 5.0
Diện Tích Hình Tròn: 78.53981633974483
Trong ví dụ trên, minh họa cách lớp HinhTron kế thừa các thuộc tính và phương thức từ lớp Hinh và thêm các tính năng riêng của nó.
Ⅲ. Kế thừa nhiều cấp (Multilevel Inheritance)
↳ Định nghĩa: Một lớp kế thừa từ một lớp cha, lớp cha này lại kế thừa từ một lớp cha khác và cứ thế. Tạo thành một chuỗi kế thừa nhiều cấp.

Ví dụ: Example.java
// Lớp cha Hinh
class Hinh {
String Mau;
// Constructor của lớp Hinh
public Hinh(String Mau) {
this.Mau = Mau;
}
// Phương thức để hiển thị Màu của hình
public void display() {
System.out.println("Màu: " + Mau);
}
}
// Lớp con HinhTron kế thừa từ lớp Hinh
class HinhTron extends Hinh {
double BanKinh;
// Constructor của lớp HinhTron
public HinhTron(String Mau, double BanKinh) {
super(Mau); // Gọi constructor của lớp cha
this.BanKinh = BanKinh;
}
// Phương thức để tính diện tích của hình tròn
public double getArea() {
return Math.PI * BanKinh * BanKinh;
}
// Phương thức để hiển thị thông tin của hình tròn
@Override
public void display() {
super.display(); // Gọi phương thức display của lớp cha
System.out.println("Bán Kính Hình Tròn: " + BanKinh);
System.out.println("Diện Tích Hình Tròn: " + getArea());
}
}
// Lớp con HinhTru kế thừa từ lớp HinhTron
class HinhTru extends HinhTron {
double ChieuCao;
// Constructor của lớp HinhTru
public HinhTru(String Mau, double BanKinh, double ChieuCao) {
super(Mau, BanKinh); // Gọi constructor của lớp HinhTron
this.ChieuCao = ChieuCao;
}
// Phương thức để tính thể tích của hình trụ
public double getVolume() {
return getArea() * ChieuCao;
}
// Phương thức để hiển thị thông tin của hình trụ
@Override
public void display() {
super.display(); // Gọi phương thức display của lớp HinhTron
System.out.println("Chiều cao của Hình Trụ: " + ChieuCao);
System.out.println("Thể Tích của Hình Trụ " + getVolume());
}
}
// Lớp chính để kiểm tra
public class Example {
public static void main(String[] args) {
// Tạo đối tượng HinhTru
HinhTru hinhTru = new HinhTru("Đỏ", 5.0,10.0);
// Hiển thị thông tin của hình trụ
hinhTru.display();
}
}
Kết quả của chương trình là:
Bán Kính Hình Tròn: 5.0
Diện Tích Hình Tròn: 78.53981633974483
Chiều cao của Hình Trụ: 10.0
Thể Tích của Hình Trụ 785.3981633974483
Trong ví dụ trên, minh họa cách lớp HinhTru kế thừa các thuộc tính và phương thức từ lớp HinhTron, và lớp HinhTron kế thừa từ lớp Hinh, tạo thành một chuỗi kế thừa nhiều cấp.
Ⅳ. Kế thừa thứ bậc (Hierarchical Inheritance)
↳ Định nghĩa: Nhiều lớp con kế thừa từ cùng một lớp cha.

Ví dụ: Example.java
// Lớp cha Hinh
class Hinh {
String Mau;
// Constructor của lớp Hinh
public Hinh(String Mau) {
this.Mau = Mau;
}
// Phương thức để hiển thị Màu của hình
public void display() {
System.out.println("Màu: " + Mau);
}
}
// Lớp con HinhTron kế thừa từ lớp Hinh
class HinhTron extends Hinh {
double BanKinh;
// Constructor của lớp HinhTron
public HinhTron(String Mau, double BanKinh) {
super(Mau); // Gọi constructor của lớp cha
this.BanKinh = BanKinh;
}
// Phương thức để tính diện tích của hình tròn
public double getArea() {
return Math.PI * BanKinh * BanKinh;
}
// Phương thức để hiển thị thông tin của hình tròn
@Override
public void display() {
super.display(); // Gọi phương thức display của lớp cha
System.out.println("Bán Kính Hình Tròn: " + BanKinh);
System.out.println("Diện Tích Hình Tròn: " + getArea());
}
}
// Lớp con HinhChuNhat kế thừa từ lớp Hinh
class HinhChuNhat extends Hinh {
double Dai, Rong;
// Constructor của lớp HinhChuNhat
public HinhChuNhat(String Mau, double Dai, double Rong) {
super(Mau); // Gọi constructor của lớp cha
this.Dai = Dai;
this.Rong = Rong;
}
// Phương thức để tính diện tích của hình chữ nhật
public double getArea() {
return Dai * Rong;
}
// Phương thức để hiển thị thông tin của hình chữ nhật
@Override
public void display() {
super.display(); // Gọi phương thức display của lớp cha
System.out.println("chiều dài Hình Chữ Nhật: " + Dai);
System.out.println("chiều Rộng Hình Chữ Nhật: " + Rong);
System.out.println("Diện Tích Hình Chữ Nhật: " + getArea());
}
}
// Lớp chính để kiểm tra
public class Example {
public static void main(String[] args) {
// Tạo đối tượng HinhTron
HinhTron hinhtron = new HinhTron("Tím", 5.0);
// Hiển thị thông tin của hình tròn
hinhtron.display();
System.out.println();
// Tạo đối tượng HinhChuNhat
HinhChuNhat hinhchunhat = new HinhChuNhat("Xanh", 10.0,5.0);
// Hiển thị thông tin của hình chữ nhật
hinhchunhat.display();
}
}
Kết quả của chương trình là:
Bán Kính Hình Tròn: 5.0
Diện Tích Hình Tròn: 78.53981633974483
Màu: Xanh
chiều dài Hình Chữ Nhật: 10.0
chiều Rộng Hình Chữ Nhật: 5.0
Diện Tích Hình Chữ Nhật: 50.0
Trong ví dụ trên, minh họa cách các lớp HinhTron và HinhChuNhat kế thừa các thuộc tính và phương thức từ lớp Hinh và thêm các tính năng riêng của chúng.
Ⅴ. Tại sao Java không hỗ trợ đa kế thừa trực tiếp qua lớp?
Java không hỗ trợ đa kế thừa trực tiếp qua lớp để tránh các vấn đề phức tạp và xung đột tiềm ẩn mà đa kế thừa có thể gây ra. Một trong những vấn đề nổi bật nhất là "Diamond Problem" (Vấn đề kim cương), dưới đây là một lý giải chi tiết:
Vấn đề kim cương (Diamond Problem)
↳ Giả sử bạn có 4 lớp: A, B, C, và D.
↳ Lớp B và C đều kế thừa từ lớp A.
↳ Lớp D kế thừa cả lớp B và lớp C.
Trong trường hợp này, lớp D sẽ có hai bản sao của tất cả các phương thức và biến từ lớp A (thông qua lớp B và lớp C). Nếu lớp A có một phương thức, lớp D sẽ không biết chính xác phải sử dụng phiên bản nào của phương thức đó — phiên bản từ B hay từ C? Điều này dẫn đến sự mơ hồ và xung đột.

Ví dụ: Example.java
class A {
void show() {
System.out.println("Hello from A");
}
}
class B extends A {
void show() {
System.out.println("Hello from B");
}
}
class C extends A {
void show() {
System.out.println("Hello from C");
}
}
// Giả sử Java hỗ trợ đa kế thừa qua các lớp
class D extends B, C {
// Không rõ phải gọi phương thức show() của B hay C?
}
public class Example {
public static void main(String[] args) {
D obj = new D();
obj.show(); // Lỗi: Mơ hồ, không rõ gọi phương thức show() của B hay C
}
}
Kết quả của chương trình là:
Giải pháp thay thế của Java
Thay vì hỗ trợ đa kế thừa qua các lớp, Java cung cấp hai giải pháp thay thế:
↳ Đa kế thừa qua Interface: Java cho phép một lớp triển khai nhiều interface. Điều này cho phép đa kế thừa mà không gặp phải các vấn đề xung đột của đa kế thừa qua các lớp, vì interface chỉ chứa các phương thức trừu tượng (không có thân phương thức) và không có trạng thái (biến instance).
↳ Sử dụng các lớp trừu tượng (Abstract Classes): Nếu bạn cần chia sẻ một phần của một lớp giữa nhiều lớp khác mà không cần sự trùng lặp hoặc xung đột, bạn có thể sử dụng các lớp trừu tượng và triển khai các lớp cụ thể từ đó.