Kế Thừa Java (`extends`): "Đẻ" Code Sao Cho Ngầu?
Java – OOP

Kế Thừa Java (`extends`): "Đẻ" Code Sao Cho Ngầu?

Author

Admin System

@root

Ngày xuất bản

20 Mar, 2026

Lượt xem

2 Lượt

extends keyword

Rồi, các chiến thần code GenZ đâu rồi, lại đây anh Creyt kể chuyện nghe! Hôm nay chúng ta sẽ "bóc phốt" một từ khóa mà nhìn thì đơn giản mà sức mạnh thì "khủng long bạo chúa" trong Java: extends. Nghe tên thôi đã thấy mùi "kế thừa" rồi đúng không? Chính xác!

extends là gì mà "hot" vậy anh Creyt?

Trong thế giới lập trình hướng đối tượng (OOP) của Java, extends chính là tấm vé vàng để bạn thực hiện "kế thừa" (Inheritance). Cứ hình dung thế này: nhà mình có ông bà, bố mẹ rồi đến mình. Mình thì "kế thừa" một phần gen từ bố mẹ, bố mẹ lại "kế thừa" từ ông bà. Mình vẫn là mình, nhưng có những đặc điểm, tính cách giống bố mẹ, ông bà đúng không?

Trong code cũng vậy. Khi một class (lớp) A extends một class B, điều đó có nghĩa là class A sẽ "kế thừa" toàn bộ các thuộc tính (fields) và phương thức (methods) mà class B có (trừ những cái privateB giấu kỹ như "mật khẩu wifi" nhà nó). Class A lúc này được gọi là lớp con (subclass/child class), còn class Blớp cha (superclass/parent class).

Nôm na, extends giúp bạn tạo ra một mối quan hệ "là một" (is-a relationship). Ví dụ: Một Chó là một ĐộngVật. Một ÔTô là một PhươngTiện.

Để làm gì? Đơn giản thôi:

  1. Tái sử dụng code (Code Reusability): Thay vì viết đi viết lại những đoạn code giống nhau cho các đối tượng có chung đặc điểm, bạn chỉ cần định nghĩa chúng ở lớp cha một lần. Các lớp con cứ thế mà "xài ké", tiết kiệm thời gian và công sức như "hack game" vậy.
  2. Tổ chức code (Code Organization): Giúp cấu trúc chương trình rõ ràng, dễ hiểu hơn, tạo ra một hệ thống phân cấp logic. Nhìn vào là biết thằng nào "con", thằng nào "cha", thằng nào "ông nội".
  3. Mở rộng tính năng (Extensibility): Khi muốn thêm một loại đối tượng mới có những đặc điểm chung với đối tượng đã có, bạn chỉ cần extends lớp cha và thêm các tính năng riêng biệt vào lớp con, không ảnh hưởng đến lớp cha.

Code Ví Dụ Minh Họa: Gia đình Động Vật

Giờ thì chiến đấu với code thôi! Anh em mình sẽ xây dựng một "hệ sinh thái" động vật đơn giản.

Đầu tiên là lớp cha DongVat:

// Lớp cha: DongVat
class DongVat {
    String ten;
    int tuoi;

    public DongVat(String ten, int tuoi) {
        this.ten = ten;
        this.tuoi = tuoi;
    }

    public void an() {
        System.out.println(ten + " đang ăn...");
    }

    public void ngu() {
        System.out.println(ten + " đang ngủ...");
    }

    public void hienThiThongTin() {
        System.out.println("Tên: " + ten + ", Tuổi: " + tuoi + " tuổi.");
    }
}

Giờ đến lớp con ChoMeo, chúng nó sẽ extends từ DongVat:

// Lớp con: Cho (extends DongVat)
class Cho extends DongVat {
    String giong;

    public Cho(String ten, int tuoi, String giong) {
        // Gọi constructor của lớp cha DongVat
        super(ten, tuoi); 
        this.giong = giong;
    }

    // Phương thức riêng của Cho
    public void sua() {
        System.out.println(ten + " đang sủa: Gâu gâu!");
    }

    // Ghi đè (override) phương thức an() từ lớp cha
    @Override
    public void an() {
        System.out.println(ten + " đang ăn xương...");
    }
    
    // Ghi đè phương thức hienThiThongTin() để thêm thông tin giống
    @Override
    public void hienThiThongTin() {
        super.hienThiThongTin(); // Gọi phương thức của lớp cha
        System.out.println("Giống: " + giong);
    }
}

// Lớp con: Meo (extends DongVat)
class Meo extends DongVat {
    String mauLong;

    public Meo(String ten, int tuoi, String mauLong) {
        super(ten, tuoi);
        this.mauLong = mauLong;
    }

    // Phương thức riêng của Meo
    public void keu() {
        System.out.println(ten + " đang kêu: Meo meo!");
    }

    // Ghi đè phương thức an() từ lớp cha
    @Override
    public void an() {
        System.out.println(ten + " đang ăn cá...");
    }
}

Và đây là cách chúng ta sử dụng chúng:

public class BaiHocExtends {
    public static void main(String[] args) {
        // Tạo đối tượng DongVat
        DongVat dongVatChung = new DongVat("Động vật chung", 5);
        dongVatChung.hienThiThongTin();
        dongVatChung.an();
        System.out.println("---");

        // Tạo đối tượng Cho
        Cho buddy = new Cho("Buddy", 3, "Golden Retriever");
        buddy.hienThiThongTin(); // Kế thừa từ DongVat và thêm của Cho
        buddy.an();             // Phương thức đã ghi đè
        buddy.ngu();            // Kế thừa từ DongVat
        buddy.sua();            // Phương thức riêng của Cho
        System.out.println("---");

        // Tạo đối tượng Meo
        Meo mun = new Meo("Mun", 2, "Trắng");
        mun.hienThiThongTin(); // Kế thừa từ DongVat
        mun.an();             // Phương thức đã ghi đè
        mun.ngu();            // Kế thừa từ DongVat
        mun.keu();            // Phương thức riêng của Meo
        System.out.println("---");
        
        // Tính đa hình (Polymorphism):
        // Một đối tượng lớp con có thể được gán cho biến kiểu lớp cha
        DongVat pet1 = new Cho("Mực", 4, "Phú Quốc");
        DongVat pet2 = new Meo("Miu", 1, "Vàng");
        
        System.out.println("Thử nghiệm đa hình:");
        pet1.an(); // Gọi phương thức an() của Cho
        pet2.an(); // Gọi phương thức an() của Meo
        // pet1.sua(); // Lỗi! Biến pet1 kiểu DongVat không biết sua()
    }
}

Giải thích nhanh:

  • super(ten, tuoi);: Dòng này trong constructor của lớp con dùng để gọi constructor của lớp cha. Bắt buộc phải là dòng đầu tiên trong constructor của lớp con nếu lớp cha có constructor có tham số.
  • @Override: Đây là một annotation (chú thích) cho biết bạn đang ghi đè (thay đổi cách hoạt động) một phương thức từ lớp cha. Nó giúp compiler kiểm tra xem bạn có ghi đè đúng không, tránh sai sót. Cứ dùng đi, nó "ngầu" và an toàn.
  • super.hienThiThongTin();: Trong phương thức ghi đè, bạn vẫn có thể gọi lại phương thức gốc của lớp cha nếu muốn tái sử dụng một phần logic của nó.
Illustration

Mẹo và Best Practices từ anh Creyt (đừng bỏ qua!)

  1. "Is-a" Relationship là chìa khóa: Chỉ dùng extends khi có mối quan hệ "là một". Một XeĐạp là một PhươngTiện, nhưng một BánhXe thì không phải là một PhươngTiện (nó là một bộ phận của PhươngTiện). Đừng nhầm lẫn giữa "là một" và "có một" (has-a).
  2. Đừng "đẻ" quá nhiều tầng: Cây gia phả càng dài, càng khó quản lý. Tương tự, nếu bạn có một chuỗi kế thừa quá sâu (ví dụ: A extends B extends C extends D...), code sẽ rất khó đọc, khó bảo trì và dễ gây ra "side effect" không mong muốn. Thường thì 2-3 tầng là đẹp, hơn nữa thì nên xem xét lại.
  3. Ưu tiên Composition over Inheritance (Thành phần hơn Kế thừa): Đây là một "kim chỉ nam" trong OOP. Nếu bạn thấy mối quan hệ là "có một" (has-a), hãy dùng thành phần (composition) thay vì kế thừa. Ví dụ: một ÔTô có một ĐộngCơ, chứ ÔTô không extends ĐộngCơ. Anh em GenZ nên tìm hiểu thêm về Composition, nó là "vũ khí bí mật" của các pro coder đấy.
  4. Sử dụng protected cẩn thận: Các thành viên protected của lớp cha sẽ được kế thừa và truy cập trực tiếp bởi lớp con. Còn private thì "bất khả xâm phạm" từ lớp con. Hãy cân nhắc kỹ quyền truy cập.

Ứng dụng thực tế: extends có ở đâu ngoài đời?

Nói suông thì khó tin đúng không? extends xuất hiện khắp nơi trong các ứng dụng bạn dùng hàng ngày:

  • Android App Development: Hầu hết các Activity, Fragment, View bạn tạo đều extends từ các lớp cơ sở của Android SDK (ví dụ: MainActivity extends AppCompatActivity). Các lớp này cung cấp sẵn rất nhiều chức năng cơ bản, bạn chỉ việc "kế thừa" và tùy chỉnh.
  • Java Swing/AWT (UI Frameworks): Khi bạn tạo một nút bấm (JButton), một khung cửa sổ (JFrame), chúng đều extends từ các lớp UI cơ bản như JComponent hay Window, mang theo các đặc tính và hành vi chung của một phần tử giao diện.
  • Các Framework Backend (Spring, Hibernate): Bạn sẽ thường xuyên thấy các lớp controller, service, repository extends từ các lớp cơ sở của framework để có được các tính năng chung như xử lý request, quản lý transaction, v.v.
  • Game Engines: Trong các game, thường có một lớp GameObject cơ bản, sau đó các nhân vật (Player), kẻ thù (Enemy), vật phẩm (Item) đều extends từ GameObject để có các thuộc tính chung như vị trí, vận tốc, khả năng va chạm.

Thử nghiệm đã từng và lời khuyên của Creyt

Anh Creyt đã từng "vọc" đủ trò với extends. Hồi mới học, anh cũng từng cố gắng xây dựng một cây kế thừa "khủng khiếp" với hàng chục tầng, nghĩ là code sẽ "xịn xò". Kết quả là sau này sửa một lỗi nhỏ thôi cũng phải "đào bới" cả cái cây, mệt mỏi và dễ gây bug kinh khủng. Đó là bài học xương máu về việc "đừng lạm dụng kế thừa".

Nên dùng extends khi nào?

  • Khi bạn muốn tạo các phiên bản chuyên biệt của một đối tượng chung: Như ví dụ DongVatCho, Meo.
  • Khi bạn muốn tái sử dụng một tập hợp lớn các phương thức và thuộc tính mà không cần thay đổi chúng quá nhiều: Ví dụ, các lớp tiện ích (Utility classes) có thể được kế thừa để thêm các phương thức chuyên biệt hơn.
  • Khi bạn cần áp dụng tính đa hình (Polymorphism): Để có thể xử lý các đối tượng con thông qua tham chiếu của lớp cha, giúp code linh hoạt và dễ mở rộng hơn (như ví dụ DongVat pet1 = new Cho(...)).

Tóm lại: extends là một công cụ cực mạnh trong OOP Java, giúp bạn xây dựng các hệ thống có cấu trúc, linh hoạt và tái sử dụng code hiệu quả. Tuy nhiên, hãy dùng nó một cách thông minh, đúng chỗ, đừng biến nó thành "con dao hai lưỡi" nhé các GenZ. Hãy nhớ câu thần chú "Is-a" và "Composition over Inheritance"! Chúc các bạn code "mượt" như lướt TikTok!

Thuộc Series: Java – OOP

Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

#tech #cyberpunk #laravel
Chỉnh sửa bài viết

Bình luận (0)

Vui lòng Đăng Nhập để Bình luận

Hỗ trợ Markdown cơ bản
Nguyễn Văn A
1 ngày trước

Tính năng này đỉnh quá ad ơi, chờ mãi mới thấy một blog Tiếng Việt có UI/UX xịn như vầy!