99 Ngày Java — Ngày 14: Kế thừa (Inheritance)
Trụ cột thứ hai của OOP lên sàn: kế thừa (inheritance). Bài toán quen thuộc: LapTrinhVien, KeToan, QuanLy — cả ba đều có tên, có lương, biết tự giới thiệu; copy code ba lần là tự chuốc họa (sửa một chỗ, quên hai chỗ). Kế thừa cho phép gom phần chung vào lớp cha, để các lớp con extends — nhận trọn gia tài rồi thêm của riêng và ghi đè thứ muốn làm khác. Kèm một lời răn quan trọng không kém: khi nào đừng kế thừa.

extends — nhận gia tài
public class NhanVien { // lớp CHA (superclass)
protected String ten; // protected: con nhìn thấy — Ngày 13!
protected double luongCoBan;
public NhanVien(String ten, double luongCoBan) {
this.ten = ten;
this.luongCoBan = luongCoBan;
}
public double tinhLuong() { return luongCoBan; }
public String gioiThieu() { return "Tôi là " + ten; }
}
public class LapTrinhVien extends NhanVien { // lớp CON — "LÀ MỘT" nhân viên
private String ngonNgu; // + field RIÊNG
public LapTrinhVien(String ten, double luong, String ngonNgu) {
super(ten, luong); // gọi constructor CHA — dòng đầu tiên!
this.ngonNgu = ngonNgu;
}
public void deployThuSau() { } // + method RIÊNG
}
LapTrinhVien dev = new LapTrinhVien("An", 20_000_000, "Java");
dev.gioiThieu(); // dùng CHÙA method của cha — không viết lại dòng nào
dev.tinhLuong(); // 20_000_000Lớp con nhận toàn bộ field + method (trừ constructor) của cha, rồi cứ thế cơi nới: thêm
ngonNgu, thêmdeployThuSau.protected— mảnh ghép Ngày 13 còn nợ: nhưprivatenhưng mở cho lớp con (và cùng package). Cân nhắc:private+ getter vẫn thường là lựa chọn kín đáo hơn.Java là đơn kế thừa: mỗi lớp chỉ
extendsmột cha — không có chuyện hai cha như C++. (Muốn "nhiều vai" thì chờ interface — Ngày 17.)Không
extendsai cả? Bạn vẫn đang kế thừa: mọi lớp đều ngầm nối vềObject— tổ tiên chung cấptoString,equals,hashCode(đại tiệc Ngày 24).
Override — viết lại cho hợp vai
public class LapTrinhVien extends NhanVien {
private double phuCapDuAn;
@Override // LUÔN dán nhãn này khi ghi đè!
public double tinhLuong() {
return super.tinhLuong() + phuCapDuAn; // super. = mượn bản CỦA CHA rồi cộng thêm
}
@Override
public String gioiThieu() {
return super.gioiThieu() + " — lập trình viên " + ngonNgu;
}
}
// @Override giúp compiler BẮT LỖI GÕ NHẦM:
// tinhLuog() → không có @Override: thành method MỚI, bug im lặng!
// tinhLuog() + @Override → lỗi biên dịch ngay: "does not override"
// OVERRIDE ≠ OVERLOAD (Ngày 10):
// override: lớp con thay RUỘT method cha — CÙNG chữ ký
// overload: cùng tên KHÁC tham số — trong cùng một lớp cũng đượcOverride = giữ nguyên chữ ký, thay ruột:
tinhLuongcủa dev khác kế toán là chuyện nghiệp vụ tự nhiên.super.cho phép mượn bản gốc rồi gia cố — mẫu "lấy của cha cộng thêm" phổ biến hơn nhiều so với đập đi xây lại.@Overridelà dây an toàn miễn phí: gõ nhầm tên/chữ ký là compiler tuýt còi ngay thay vì để một method "song sinh" âm thầm ra đời — dán nó mọi lúc, không ngoại lệ.Phân biệt dứt điểm cặp gây nhiễu số một: override — cùng chữ ký, quan hệ cha-con; overload (Ngày 10) — cùng tên khác tham số. Phỏng vấn hỏi hoài không chán.
super(...) — cha khai sinh trước
// Thứ tự khai sinh khi new LapTrinhVien(...): CHA TRƯỚC, CON SAU
//
// new LapTrinhVien("An", 20tr, "Java")
// → constructor LapTrinhVien chạy super(ten, luong) ← BẮT BUỘC dòng đầu
// → constructor NhanVien khởi tạo ten, luongCoBan ← phần "cha" xong trước
// → phần thân còn lại: this.ngonNgu = ngonNgu ← rồi mới tới phần "con"
//
// Không viết super(...)? Compiler tự chèn super() KHÔNG THAM SỐ.
// Nhưng nếu cha KHÔNG CÓ constructor không tham số (quà đã bị rút — Ngày 12!)
// → lỗi biên dịch: bạn PHẢI gọi super(...) tường minh.
//
// Ghép nối quen thuộc: this(...) hoặc super(...) — chỉ MỘT trong hai, luôn dòng đầu.Logic rất đời: muốn dựng phần "lập trình viên" thì phần "nhân viên" bên dưới phải xong trước — nền trước, nhà sau. Toàn bộ luật Ngày 12 tái xuất nguyên bộ: quà default constructor, dòng đầu tiên, và dây chuyền ủy thác — chỉ thêm một tầng cha-con.
Khi nào ĐỪNG kế thừa: is-a vs has-a
// "LÀ MỘT" (is-a) — phép thử trước khi extends:
// LapTrinhVien LÀ MỘT NhanVien? ✓ hợp lý → extends
// Oto LÀ MỘT DongCo? ✗ Ô tô CÓ động cơ — không PHẢI động cơ!
public class Oto {
private DongCo dongCo; // ✓ COMPOSITION — "CÓ MỘT" (has-a): field, không extends
private BanhXe[] banhXe;
}
// Vì sao lạm dụng kế thừa nguy hiểm?
// - Con dính CHẶT vào ruột cha: cha đổi nội thất, con sập theo (fragile base class)
// - Java CHỈ CHO extends MỘT lớp — dùng "vé duy nhất" cho đúng quan hệ is-a thật
// - Móc nối hành vi linh hoạt → composition + interface (Ngày 17) thường đúng hơn
// Châm ngôn khắc cốt: "favor composition over inheritance"Phép thử một câu trước mỗi
extends: đọc to "X là một Y" — nghe gượng là sai quan hệ, chuyển sang has-a (field) ngay.Kế thừa là khớp nối cứng nhất giữa hai lớp: con thấy
protected, phụ thuộc thứ tự khởi tạo, vỡ theo mọi thay đổi của cha. Sức mạnh lớn — giá cũng lớn.Kinh nghiệm thực chiến: cây kế thừa nông (1–2 tầng) là lành mạnh; cây 5 tầng
A extends B extends C...là mê cung debug. Nghi ngờ thì composition trước.
Bài tập nhỏ
Dựng
NhanVienvới hai conLapTrinhVien(phụ cấp dự án) vàKeToan(thưởng quyết toán) — mỗi con overridetinhLuongmột kiểu, có dùngsuper..Cố tình bỏ
super(...)trong constructor con khi cha không có bản không tham số — đọc kỹ lỗi, rồi sửa.Gõ nhầm
tinhLuogkhông có@Override— quan sát bug im lặng; thêm@Overridevà xem compiler cứu bạn.Xét
Oto/DongCo,HinhVuong/HinhChuNhat,ArrayList/List— quan hệ nào là is-a thật? (Câu giữa gài bẫy đấy — thử overridesetChieuDaicủa hình vuông xem có ổn không.)
Kết luận
Trụ cột thứ hai đã đứng: extends trao gia tài từ cha xuống con, super gọi ngược lên cả ở constructor lẫn method, @Override gác cổng chữ ký, protected hé cửa đúng mức — và phép thử is-a giữ bạn khỏi lạm dụng. Nhưng kho báu thật sự của kế thừa chưa lộ diện: khi NhanVien nv = new LapTrinhVien(...) — biến kiểu cha cầm đối tượng con — thì nv.tinhLuong() chạy bản của ai? Câu trả lời là đa hình (polymorphism) — trụ cột đẹp nhất của OOP, trọn vẹn ở Ngày 15. Hẹn gặp lại!
Site Admin
Engineer and writer. Building things with TypeScript and distributed systems.
Bình luận (0)
Bạn cần đăng nhập bằng Google để bình luận.
Hãy là người bình luận đầu tiên.


