Skip to main content

🔀 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:

  1. Số lượng tham số
  2. Kiểu tham số
  3. 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

  1. Cùng chức năng: Chỉ overload method có cùng mục đích
  2. Khác tham số rõ ràng: Tránh ambiguous
  3. Method ngắn nhất: Gọi method dài nhất để tái sử dụng code
  4. Đặt tên nhất quán: Dễ nhớ, dễ sử dụng
  5. Tránh quá nhiều: Không overload quá 4-5 lần
  6. 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:

👉 Constructor - Hàm Khởi Tạo


💡 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!