🌳 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
- super() đầu tiên: Gọi constructor cha trước tiên
- protected cho con: Dùng protected nếu muốn con truy cập
- @Override rõ ràng: Luôn dùng @Override khi override
- Không đa kế thừa: Java chỉ kế thừa 1 class
- instanceof cẩn thận: Kiểm tra kiểu trước khi cast
- 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!