Skip to main content

🌳 Kế Thừa - Extends

Chào mừng đến với bài học về Extends - kế thừa công thức từ quán chính trong Café!

🎯 Món Ăn Hôm Nay

Tưởng tượng bạn mở chi nhánh cà phê:

  • Quán chính → Class cha (DoUong)
  • Chi nhánh → Class con (Latte, Mocha)
  • Thừa hưởng quy trình → Không cần viết lại
  • Tùy chỉnh riêng → Thêm đặc điểm mới

Đó chính là Extends - kế thừa thuộc tính và method!

🌳 Extends Là Gì?

Extends = Kế thừa (class con thừa hưởng từ class cha)

// Class cha (Parent/Superclass)
public class DoUong {
String ten;
int gia;

public void hienThi() {
System.out.println(ten + ": " + gia + " VND");
}
}

// Class con (Child/Subclass) kế thừa từ DoUong
public class Latte extends DoUong {
String loaiSua; // Thuộc tính riêng

// Kế thừa: ten, gia, hienThi()
// Thêm mới: loaiSua
}

Ẩn Dụ Café:

  • Class cha = Quán chính (công thức gốc)
  • Class con = Chi nhánh (kế thừa + tùy chỉnh)
  • extends = Mở chi nhánh từ quán chính
  • super = Gọi về quán chính
  • Override = Điều chỉnh công thức riêng

📋 Cấu Trúc Kế Thừa

public class ClassCha {
// Thuộc tính và method của cha
}

public class ClassCon extends ClassCha {
// Kế thừa tất cả từ cha
// Thêm thuộc tính/method mới
// Override method của cha
}

👨‍🍳 Câu Chuyện Trong Quán

Tình huống 1: Kế Thừa Cơ Bản

// Class cha
public class DoUong {
String ten;
int gia;

public DoUong(String ten, int gia) {
this.ten = ten;
this.gia = gia;
}

public void hienThi() {
System.out.printf("☕ %s: %,d VND%n", ten, gia);
}
}

// Class con kế thừa DoUong
public class CaPheNong extends DoUong {
int nhietDo; // Thuộc tính riêng

public CaPheNong(String ten, int gia, int nhietDo) {
super(ten, gia); // Gọi constructor cha
this.nhietDo = nhietDo;
}

// Method riêng
public void kiemTraNhietDo() {
System.out.println("🌡️ Nhiệt độ: " + nhietDo + "°C");
}
}

// Sử dụng
public class TestKeThua {
public static void main(String[] args) {
CaPheNong latte = new CaPheNong("Latte", 50000, 80);

latte.hienThi(); // Method từ cha
latte.kiemTraNhietDo(); // Method riêng
}
}

Output:

☕ Latte: 50,000 VND
🌡️ Nhiệt độ: 80°C

Tình huống 2: Phân Cấp Nhiều Tầng

// Tầng 1: Class gốc
public class SanPham {
String ma;
String ten;

public SanPham(String ma, String ten) {
this.ma = ma;
this.ten = ten;
}
}

// Tầng 2: Kế thừa SanPham
public class DoUong extends SanPham {
int gia;

public DoUong(String ma, String ten, int gia) {
super(ma, ten);
this.gia = gia;
}
}

// Tầng 3: Kế thừa DoUong
public class CaPhe extends DoUong {
String loaiHat;

public CaPhe(String ma, String ten, int gia, String loaiHat) {
super(ma, ten, gia);
this.loaiHat = loaiHat;
}

public void hienThi() {
System.out.printf("Mã: %s | %s | %,d VND | Hạt: %s%n",
ma, ten, gia, loaiHat);
}
}

// Sử dụng
public class TestPhanCap {
public static void main(String[] args) {
CaPhe espresso = new CaPhe("CP001", "Espresso", 45000, "Arabica");
espresso.hienThi();
}
}

📝 Công Thức Nấu (Code Examples)

Ví Dụ 1: super - Gọi Constructor Cha

public class NhanVien {
String ten;
int luong;

public NhanVien(String ten, int luong) {
this.ten = ten;
this.luong = luong;
System.out.println("✅ Tạo nhân viên: " + ten);
}
}

public class Barista extends NhanVien {
String chuyenMon;

public Barista(String ten, int luong, String chuyenMon) {
super(ten, luong); // Gọi constructor cha
this.chuyenMon = chuyenMon;
System.out.println("☕ Chuyên môn: " + chuyenMon);
}

public void hienThi() {
System.out.printf("👤 %s - %s: %,d VND%n",
ten, chuyenMon, luong);
}
}

// Sử dụng
public class TestSuper {
public static void main(String[] args) {
Barista nv = new Barista("Minh", 7000000, "Pha chế");
nv.hienThi();
}
}

Output:

✅ Tạo nhân viên: Minh
☕ Chuyên môn: Pha chế
👤 Minh - Pha chế: 7,000,000 VND

Ví Dụ 2: Kế Thừa Method

public class QuanCafe {
String ten;
String diaChi;

public QuanCafe(String ten, String diaChi) {
this.ten = ten;
this.diaChi = diaChi;
}

public void moC ua() {
System.out.println("🏪 " + ten + " đang mở cửa");
}

public void dongCua() {
System.out.println("🔒 " + ten + " đã đóng cửa");
}
}

public class QuanCaPheVIP extends QuanCafe {
int soGheVIP;

public QuanCaPheVIP(String ten, String diaChi, int soGheVIP) {
super(ten, diaChi);
this.soGheVIP = soGheVIP;
}

// Method riêng
public void phucVuVIP() {
System.out.println("🌟 Khu VIP (" + soGheVIP + " ghế) sẵn sàng");
}
}

// Sử dụng
public class TestMethod {
public static void main(String[] args) {
QuanCaPheVIP quanVIP = new QuanCaPheVIP("Java Café VIP", "Quận 1", 10);

quanVIP.moCua(); // Từ cha
quanVIP.phucVuVIP(); // Riêng
quanVIP.dongCua(); // Từ cha
}
}

Ví Dụ 3: Override Method

public class DoUong {
String ten;
int gia;

public DoUong(String ten, int gia) {
this.ten = ten;
this.gia = gia;
}

public void pha() {
System.out.println("☕ Pha " + ten + " theo công thức chuẩn");
}

public int layGia() {
return gia;
}
}

public class Latte extends DoUong {
public Latte(int gia) {
super("Latte", gia);
}

// Override method pha()
@Override
public void pha() {
System.out.println("☕ Pha Latte:");
System.out.println(" 1. Pha Espresso");
System.out.println(" 2. Thêm sữa nóng");
System.out.println(" 3. Tạo lớp foam");
}
}

public class Mocha extends DoUong {
public Mocha(int gia) {
super("Mocha", gia);
}

@Override
public void pha() {
System.out.println("🍫 Pha Mocha:");
System.out.println(" 1. Pha Espresso");
System.out.println(" 2. Thêm chocolate");
System.out.println(" 3. Thêm sữa");
}
}

// Sử dụng
public class TestOverride {
public static void main(String[] args) {
DoUong d1 = new Latte(50000);
DoUong d2 = new Mocha(60000);

d1.pha();
System.out.println();
d2.pha();
}
}

Output:

☕ Pha Latte:
1. Pha Espresso
2. Thêm sữa nóng
3. Tạo lớp foam

🍫 Pha Mocha:
1. Pha Espresso
2. Thêm chocolate
3. Thêm sữa

Ví Dụ 4: super - Gọi Method Cha

public class ThucDon {
String ten;

public ThucDon(String ten) {
this.ten = ten;
}

public void hienThi() {
System.out.println("📋 Thực đơn: " + ten);
}
}

public class ThucDonDacBiet extends ThucDon {
String[] monMoi;

public ThucDonDacBiet(String ten, String[] monMoi) {
super(ten);
this.monMoi = monMoi;
}

@Override
public void hienThi() {
super.hienThi(); // Gọi method cha

System.out.println("🌟 Món đặc biệt:");
for (String mon : monMoi) {
System.out.println(" - " + mon);
}
}
}

// Sử dụng
public class TestSuperMethod {
public static void main(String[] args) {
String[] dacBiet = {"Latte Lavender", "Mocha Mint"};
ThucDonDacBiet menu = new ThucDonDacBiet("Thực đơn tháng 10", dacBiet);

menu.hienThi();
}
}

Output:

📋 Thực đơn: Thực đơn tháng 10
🌟 Món đặc biệt:
- Latte Lavender
- Mocha Mint

Ví Dụ 5: Protected Access Modifier

public class DoUong {
protected String ten; // Truy cập được từ class con
private int giaNhap; // Không truy cập từ class con
public int giaBan; // Truy cập mọi nơi

public DoUong(String ten, int giaNhap, int giaBan) {
this.ten = ten;
this.giaNhap = giaNhap;
this.giaBan = giaBan;
}

protected int tinhLoiNhuan() {
return giaBan - giaNhap;
}
}

public class CaPheProTuoi extends DoUong {
private int phiVanChuyen;

public CaPheProTuoi(String ten, int giaNhap, int giaBan, int phiVC) {
super(ten, giaNhap, giaBan);
this.phiVanChuyen = phiVC;
}

public void hienThiThongTin() {
System.out.println("Tên: " + ten); // ✅ OK - protected
// System.out.println("Nhập: " + giaNhap); // ❌ Lỗi - private
System.out.println("Bán: " + giaBan); // ✅ OK - public

int loiNhuan = tinhLoiNhuan() - phiVanChuyen; // ✅ OK - protected method
System.out.println("Lợi nhuận: " + loiNhuan);
}
}

Ví Dụ 6: instanceof - Kiểm Tra Kiểu

public class DoUong {
String ten;
public DoUong(String ten) { this.ten = ten; }
}

public class CaPhe extends DoUong {
public CaPhe(String ten) { super(ten); }
}

public class TraSua extends DoUong {
public TraSua(String ten) { super(ten); }
}

public class TestInstanceOf {
public static void phucVu(DoUong doUong) {
if (doUong instanceof CaPhe) {
System.out.println("☕ Pha cà phê: " + doUong.ten);
} else if (doUong instanceof TraSua) {
System.out.println("🧋 Pha trà sữa: " + doUong.ten);
} else {
System.out.println("🥤 Pha đồ uống: " + doUong.ten);
}
}

public static void main(String[] args) {
DoUong d1 = new CaPhe("Latte");
DoUong d2 = new TraSua("Trà sữa trân châu");
DoUong d3 = new DoUong("Nước ép");

phucVu(d1);
phucVu(d2);
phucVu(d3);
}
}

Output:

☕ Pha cà phê: Latte
🧋 Pha trà sữa: Trà sữa trân châu
🥤 Pha đồ uống: Nước ép

🔥 Thực Hành Trong Quán

Bài Tập 1: Hệ Thống Nhân Viên

public class NhanVien {
protected String ten;
protected int luongCoBan;

public NhanVien(String ten, int luongCoBan) {
this.ten = ten;
this.luongCoBan = luongCoBan;
}

public int tinhLuong() {
return luongCoBan;
}

public void hienThi() {
System.out.printf("👤 %s: %,d VND%n", ten, tinhLuong());
}
}

public class BaristaChuyenNghiep extends NhanVien {
private int soGioLamThem;
private static final int GIA_GIO = 50000;

public BaristaChuyenNghiep(String ten, int luongCoBan, int soGioLamThem) {
super(ten, luongCoBan);
this.soGioLamThem = soGioLamThem;
}

@Override
public int tinhLuong() {
return luongCoBan + (soGioLamThem * GIA_GIO);
}

@Override
public void hienThi() {
System.out.printf("☕ Barista %s:%n", ten);
System.out.printf(" Lương cơ bản: %,d VND%n", luongCoBan);
System.out.printf(" Làm thêm: %d giờ × %,d = %,d VND%n",
soGioLamThem, GIA_GIO, soGioLamThem * GIA_GIO);
System.out.printf(" Tổng: %,d VND%n", tinhLuong());
}
}

// Test
public class TestNhanVien {
public static void main(String[] args) {
NhanVien nv1 = new NhanVien("Lan", 5000000);
BaristaChuyenNghiep nv2 = new BaristaChuyenNghiep("Minh", 7000000, 20);

nv1.hienThi();
System.out.println();
nv2.hienThi();
}
}

Bài Tập 2: Quản Lý Đơn Hàng

public class DonHang {
protected String maDon;
protected int tongTien;

public DonHang(String maDon, int tongTien) {
this.maDon = maDon;
this.tongTien = tongTien;
}

public int tinhThanhToan() {
return tongTien;
}

public void inHoaDon() {
System.out.println("🧾 Mã đơn: " + maDon);
System.out.printf(" Tổng: %,d VND%n", tongTien);
System.out.printf(" Thanh toán: %,d VND%n", tinhThanhToan());
}
}

public class DonHangVIP extends DonHang {
private int phanTramGiam;

public DonHangVIP(String maDon, int tongTien, int phanTramGiam) {
super(maDon, tongTien);
this.phanTramGiam = phanTramGiam;
}

@Override
public int tinhThanhToan() {
int giamGia = tongTien * phanTramGiam / 100;
return tongTien - giamGia;
}

@Override
public void inHoaDon() {
super.inHoaDon();
int giamGia = tongTien - tinhThanhToan();
System.out.printf(" 🌟 VIP giảm %d%%: -%,d VND%n",
phanTramGiam, giamGia);
}
}

⚠️ Lỗi Thường Gặp

Lỗi 1: Quên Gọi super()

public class Cha {
String ten;
public Cha(String ten) {
this.ten = ten;
}
}

// ❌ SAI: Không gọi super()
public class Con extends Cha {
int tuoi;
public Con(String ten, int tuoi) {
// Thiếu super(ten); ❌ Lỗi!
this.tuoi = tuoi;
}
}

// ✅ ĐÚNG: Gọi super()
public class Con extends Cha {
int tuoi;
public Con(String ten, int tuoi) {
super(ten); // ✅ OK
this.tuoi = tuoi;
}
}

Lỗi 2: Truy Cập private Từ Class Con

public class Cha {
private int giaTri; // private
}

public class Con extends Cha {
public void hienThi() {
// System.out.println(giaTri); // ❌ Lỗi! private
// Dùng protected hoặc getter
}
}

Lỗi 3: Đa Kế Thừa (Java Không Hỗ Trợ)

// ❌ SAI: Java không cho phép đa kế thừa class
public class Con extends Cha1, Cha2 { // ❌ Lỗi!
}

// ✅ ĐÚNG: Dùng interface
public class Con extends Cha implements Interface1, Interface2 {
}

Lỗi 4: Override Sai Signature

public class Cha {
public void pha(String mon) {
System.out.println("Pha " + mon);
}
}

// ❌ SAI: Khác tham số
public class Con extends Cha {
@Override
public void pha(String mon, int soLuong) { // ❌ Lỗi! Khác signature
// Đây là overload, không phải override
}
}

// ✅ ĐÚNG: Cùng signature
public class Con extends Cha {
@Override
public void pha(String mon) { // ✅ OK
System.out.println("Pha " + mon + " theo cách mới");
}
}

💡 Bí Quyết Của Barista

  1. super() đầu tiên: Gọi constructor cha trước tiên
  2. protected cho con: Dùng protected nếu muốn con truy cập
  3. @Override rõ ràng: Luôn dùng @Override khi override
  4. Không đa kế thừa: Java chỉ kế thừa 1 class
  5. instanceof cẩn thận: Kiểm tra kiểu trước khi cast
  6. IS-A relationship: Chỉ kế thừa khi có quan hệ "là một"

🎓 Bạn Đã Học Được

  • Extends = Kế thừa từ class cha
  • ✅ Class con có tất cả thuộc tính/method của cha
  • super() = Gọi constructor cha
  • super.method() = Gọi method cha
  • @Override = Ghi đè method cha
  • protected = Truy cập từ class con
  • instanceof = Kiểm tra kiểu object
  • ✅ Java không hỗ trợ đa kế thừa class

☕ Món Tiếp Theo

Đã biết kế thừa! Giờ học về override chi tiết:

👉 Method Overriding - Ghi Đè Phương Thức


💡 Lời Khuyên Cuối: Kế thừa như mở chi nhánh - giữ công thức gốc nhưng tùy chỉnh theo từng nơi!