🔀 Nạp Chồng Phương Thức - Method Overloading
Chào mừng đến với bài học về Method Overloading - một tên nhiều công thức trong Café!
🎯 Món Ăn Hôm Nay
Tưởng tượng bạn có method phaCaPhe():
- phaCaPhe() → Pha mặc định (Espresso)
- phaCaPhe(String loai) → Pha theo loại
- phaCaPhe(String loai, int soLuong) → Pha theo loại và số lượng
- phaCaPhe(String loai, String size) → Pha theo loại và size
Cùng tên nhưng khác tham số - đó chính là Method Overloading!
🔀 Method Overloading Là Gì?
Method Overloading = Nạp chồng phương thức (nhiều method cùng tên, khác tham số)
public class QuanCafe {
// Method 1: Không tham số
public static void phaCaPhe() {
System.out.println("☕ Pha Espresso");
}
// Method 2: 1 tham số
public static void phaCaPhe(String loai) {
System.out.println("☕ Pha " + loai);
}
// Method 3: 2 tham số
public static void phaCaPhe(String loai, int soLuong) {
System.out.println("☕ Pha " + soLuong + " ly " + loai);
}
}
☕ Ẩn Dụ Café:
- Cùng tên = Cùng công việc (pha cà phê)
- Khác tham số = Khác chi tiết (loại, số lượng, size)
- Compiler chọn = Barista hiểu ngay cần pha gì
- Linh hoạt = Dễ sử dụng
📋 Quy Tắc Overloading
Method overloading hợp lệ khi khác:
- ✅ Số lượng tham số
- ✅ Kiểu tham số
- ✅ Thứ tự tham số
KHÔNG được khác chỉ:
- ❌ Tên tham số
- ❌ Return type
- ❌ Access modifier
// ✅ ĐÚNG: Khác số lượng tham số
public static void pha() {}
public static void pha(String loai) {}
// ✅ ĐÚNG: Khác kiểu tham số
public static void pha(int soLuong) {}
public static void pha(String loai) {}
// ❌ SAI: Chỉ khác tên tham số
public static void pha(String ten) {}
public static void pha(String loai) {} // Lỗi!
// ❌ SAI: Chỉ khác return type
public static void pha() {}
public static int pha() {} // Lỗi!
👨🍳 Câu Chuyện Trong Quán
Tình huống: Pha Cà Phê Linh Hoạt
public class PhaCaPhe {
// Pha mặc định
public static void pha() {
System.out.println("☕ Pha Espresso size M");
}
// Pha theo loại
public static void pha(String loai) {
System.out.println("☕ Pha " + loai + " size M");
}
// Pha theo loại và size
public static void pha(String loai, String size) {
System.out.println("☕ Pha " + loai + " size " + size);
}
// Pha theo loại, size và số lượng
public static void pha(String loai, String size, int soLuong) {
System.out.println("☕ Pha " + soLuong + " ly " + loai + " size " + size);
}
public static void main(String[] args) {
pha(); // Espresso M
pha("Latte"); // Latte M
pha("Mocha", "L"); // Mocha L
pha("Cappuccino", "S", 3); // 3 ly Cappuccino S
}
}
Output:
☕ Pha Espresso size M
☕ Pha Latte size M
☕ Pha Mocha size L
☕ Pha 3 ly Cappuccino size S
📝 Công Thức Nấu (Code Examples)
Ví Dụ 1: Overload Khác Số Lượng Tham Số
public class TinhTien {
// 1 tham số: Chỉ tổng tiền
public static void inHoaDon(int tongTien) {
System.out.println("💰 Tổng: " + tongTien + " VND");
}
// 2 tham số: Tổng tiền + giảm giá
public static void inHoaDon(int tongTien, int giamGia) {
int thanhToan = tongTien - giamGia;
System.out.println("💰 Tổng: " + tongTien + " VND");
System.out.println("🎁 Giảm: " + giamGia + " VND");
System.out.println("💵 Thanh toán: " + thanhToan + " VND");
}
// 3 tham số: Tổng + giảm giá + tip
public static void inHoaDon(int tongTien, int giamGia, int tip) {
int thanhToan = tongTien - giamGia + tip;
System.out.println("💰 Tổng: " + tongTien + " VND");
System.out.println("🎁 Giảm: " + giamGia + " VND");
System.out.println("💝 Tip: " + tip + " VND");
System.out.println("💵 Thanh toán: " + thanhToan + " VND");
}
public static void main(String[] args) {
inHoaDon(100000);
System.out.println();
inHoaDon(100000, 10000);
System.out.println();
inHoaDon(100000, 10000, 5000);
}
}
Ví Dụ 2: Overload Khác Kiểu Tham Số
public class GiamGia {
// Giảm theo số tiền cố định
public static int tinhGiamGia(int tongTien, int soTienGiam) {
return tongTien - soTienGiam;
}
// Giảm theo phần trăm
public static int tinhGiamGia(int tongTien, double phanTram) {
int giamGia = (int) (tongTien * phanTram / 100);
return tongTien - giamGia;
}
public static void main(String[] args) {
int tongTien = 100000;
// Gọi method 1: Giảm 10,000đ
int thanhToan1 = tinhGiamGia(tongTien, 10000);
System.out.println("Giảm cố định: " + thanhToan1 + " VND");
// Gọi method 2: Giảm 15%
int thanhToan2 = tinhGiamGia(tongTien, 15.0);
System.out.println("Giảm 15%: " + thanhToan2 + " VND");
}
}
Output:
Giảm cố định: 90000 VND
Giảm 15%: 85000 VND
Ví Dụ 3: Overload Khác Thứ Tự Tham Số
public class DatMon {
// String, int
public static void dat(String mon, int soLuong) {
System.out.println("📝 Đặt: " + soLuong + " ly " + mon);
}
// int, String
public static void dat(int soBan, String mon) {
System.out.println("📝 Bàn " + soBan + " đặt: " + mon);
}
public static void main(String[] args) {
dat("Latte", 2); // Method 1
dat(5, "Mocha"); // Method 2
}
}
Output:
📝 Đặt: 2 ly Latte
📝 Bàn 5 đặt: Mocha
Ví Dụ 4: Overload Với Varargs
public class TinhTong {
// Tính tổng 2 số
public static int tinhTong(int a, int b) {
return a + b;
}
// Tính tổng 3 số
public static int tinhTong(int a, int b, int c) {
return a + b + c;
}
// Tính tổng nhiều số (varargs)
public static int tinhTong(int... numbers) {
int tong = 0;
for (int num : numbers) {
tong += num;
}
return tong;
}
public static void main(String[] args) {
System.out.println("2 số: " + tinhTong(10, 20));
System.out.println("3 số: " + tinhTong(10, 20, 30));
System.out.println("5 số: " + tinhTong(10, 20, 30, 40, 50));
}
}
Output:
2 số: 30
3 số: 60
5 số: 150
Ví Dụ 5: Overload Thực Tế - Tạo Đơn Hàng
public class TaoDonHang {
// Đơn giản: Chỉ món
public static void taoDon(String mon) {
System.out.println("╔═══════════════════╗");
System.out.println("║ ĐƠN HÀNG ║");
System.out.println("╠═══════════════════╣");
System.out.printf("║ Món: %-12s║%n", mon);
System.out.printf("║ Số lượng: 1 ║%n");
System.out.printf("║ Size: M ║%n");
System.out.println("╚═══════════════════╝");
}
// Món + số lượng
public static void taoDon(String mon, int soLuong) {
System.out.println("╔═══════════════════╗");
System.out.println("║ ĐƠN HÀNG ║");
System.out.println("╠═══════════════════╣");
System.out.printf("║ Món: %-12s║%n", mon);
System.out.printf("║ Số lượng: %-7d║%n", soLuong);
System.out.printf("║ Size: M ║%n");
System.out.println("╚═══════════════════╝");
}
// Món + số lượng + size
public static void taoDon(String mon, int soLuong, String size) {
System.out.println("╔═══════════════════╗");
System.out.println("║ ĐƠN HÀNG ║");
System.out.println("╠═══════════════════╣");
System.out.printf("║ Món: %-12s║%n", mon);
System.out.printf("║ Số lượng: %-7d║%n", soLuong);
System.out.printf("║ Size: %-11s║%n", size);
System.out.println("╚═══════════════════╝");
}
// Đầy đủ: Món + số lượng + size + giao hàng
public static void taoDon(String mon, int soLuong, String size, boolean giaoHang) {
System.out.println("╔═══════════════════╗");
System.out.println("║ ĐƠN HÀNG ║");
System.out.println("╠═══════════════════╣");
System.out.printf("║ Món: %-12s║%n", mon);
System.out.printf("║ Số lượng: %-7d║%n", soLuong);
System.out.printf("║ Size: %-11s║%n", size);
System.out.printf("║ Giao: %-11s║%n", giaoHang ? "Có" : "Không");
System.out.println("╚═══════════════════╝");
}
public static void main(String[] args) {
taoDon("Latte");
System.out.println();
taoDon("Mocha", 2);
System.out.println();
taoDon("Espresso", 1, "L");
System.out.println();
taoDon("Cappuccino", 3, "M", true);
}
}
Ví Dụ 6: Overload Với Default Values (Mô phỏng)
public class GiaTriMacDinh {
// Method đầy đủ
public static void phaMon(String loai, String size, boolean daBot, boolean sua) {
System.out.println("☕ Pha " + loai);
System.out.println(" Size: " + size);
System.out.println(" Đá bột: " + (daBot ? "Có" : "Không"));
System.out.println(" Sữa: " + (sua ? "Có" : "Không"));
System.out.println();
}
// Overload: Chỉ loại (các giá trị khác mặc định)
public static void phaMon(String loai) {
phaMon(loai, "M", false, true); // Gọi method đầy đủ
}
// Overload: Loại + size
public static void phaMon(String loai, String size) {
phaMon(loai, size, false, true);
}
// Overload: Loại + size + đá bột
public static void phaMon(String loai, String size, boolean daBot) {
phaMon(loai, size, daBot, true);
}
public static void main(String[] args) {
phaMon("Latte"); // Mặc định: M, không đá, có sữa
phaMon("Mocha", "L"); // L, không đá, có sữa
phaMon("Espresso", "S", true); // S, có đá, có sữa
phaMon("Cappuccino", "M", false, false); // Đầy đủ
}
}
🔥 Thực Hành Trong Quán
Bài Tập 1: Overload Method In
public class InThongTin {
public static void in(String message) {
System.out.println("📝 " + message);
}
public static void in(int number) {
System.out.println("🔢 " + number);
}
public static void in(double price) {
System.out.printf("💰 %,.0f VND%n", price);
}
public static void in(boolean status) {
System.out.println("✅ " + (status ? "Mở cửa" : "Đóng cửa"));
}
public static void main(String[] args) {
in("Java Café");
in(50);
in(50000.0);
in(true);
}
}
Bài Tập 2: Overload Tính Diện Tích
public class TinhDienTich {
// Diện tích hình vuông
public static double tinhDienTich(double canh) {
return canh * canh;
}
// Diện tích hình chữ nhật
public static double tinhDienTich(double dai, double rong) {
return dai * rong;
}
// Diện tích hình tròn
public static double tinhDienTich(double banKinh, boolean laTron) {
if (laTron) {
return Math.PI * banKinh * banKinh;
}
return 0;
}
public static void main(String[] args) {
System.out.println("Vuông (5m): " + tinhDienTich(5));
System.out.println("Chữ nhật (4×6m): " + tinhDienTich(4, 6));
System.out.println("Tròn (r=3m): " + tinhDienTich(3, true));
}
}
Bài Tập 3: Overload Chuyển Đổi
public class ChuyenDoi {
// String sang int
public static int chuyenDoi(String str) {
return Integer.parseInt(str);
}
// int sang String
public static String chuyenDoi(int num) {
return String.valueOf(num);
}
// double sang int
public static int chuyenDoi(double num) {
return (int) num;
}
public static void main(String[] args) {
int a = chuyenDoi("123"); // String → int
String b = chuyenDoi(456); // int → String
int c = chuyenDoi(78.9); // double → int
System.out.println("a = " + a + " (type: int)");
System.out.println("b = " + b + " (type: String)");
System.out.println("c = " + c + " (type: int)");
}
}
⚠️ Lỗi Thường Gặp
Lỗi 1: Overload Không Rõ Ràng (Ambiguous)
// ❌ SAI: Compiler không biết chọn method nào
public static void pha(int soLuong, double gia) {}
public static void pha(double gia, int soLuong) {}
// Khi gọi:
pha(5, 50.0); // OK
pha(5, 5); // ❌ Ambiguous! Cả 2 đều khớp
Lỗi 2: Chỉ Khác Return Type
// ❌ SAI: Không được chỉ khác return type
public static int tinh() { return 10; }
public static double tinh() { return 10.0; } // ❌ Lỗi!
Lỗi 3: Chỉ Khác Tên Tham Số
// ❌ SAI: Tên tham số không quan trọng
public static void pha(String loai) {}
public static void pha(String ten) {} // ❌ Lỗi! Cùng kiểu
Lỗi 4: Nhầm Lẫn Với Varargs
// ❌ SAI: Varargs gây nhầm lẫn
public static void tinh(int a, int b) {}
public static void tinh(int... nums) {}
// Khi gọi:
tinh(1, 2); // ⚠️ Gọi method nào? (chọn method 1)
💡 Bí Quyết Của Barista
- Cùng chức năng: Chỉ overload method có cùng mục đích
- Khác tham số rõ ràng: Tránh ambiguous
- Method ngắn nhất: Gọi method dài nhất để tái sử dụng code
- Đặt tên nhất quán: Dễ nhớ, dễ sử dụng
- Tránh quá nhiều: Không overload quá 4-5 lần
- Varargs cuối cùng: Đặt varargs ở cuối danh sách tham số
🎓 Bạn Đã Học Được
- ✅ Method Overloading = Nhiều method cùng tên, khác tham số
- ✅ Khác: Số lượng, kiểu, thứ tự tham số
- ✅ Không khác: Chỉ tên tham số hoặc return type
- ✅ Compiler tự chọn method phù hợp
- ✅ Tăng tính linh hoạt
- ✅ Code dễ đọc, dễ sử dụng
- ✅ Ứng dụng: Tính toán, in, chuyển đổi
☕ Món Tiếp Theo
Đã biết overloading! Giờ học về constructor:
💡 Lời Khuyên Cuối: Overloading như menu linh hoạt - cùng món nhưng có nhiều lựa chọn size và topping!