overloading - ảnh feature

Java Bài 29: Nạp Chồng Phương Thức (Overloading)

Posted by

Chào mừng các bạn đã đến với bài học Java số 29. Bài học về Nạp chồng phương thức (Overloading). Đây là bài học trong chuỗi bài về lập trình ngôn ngữ Java của Yellow Code Books.

Nếu bạn nhớ, chúng ta đã học về overriding. Hôm nay bạn lại được biết thêm về overloading. Cẩn thận coi chừng nhầm lẫn nha bạn. Overriding, overloading, over, and over…

Mình đùa tí thôi, mình giúp các bạn nhớ lại một tí như sau.

Overriding là cái sự lớp con ghi đè phương thức của lớp cha.
Overloading là nạp chồng phương thức.

Mời các bạn cùng đến với bài học hôm nay. Bài học sẽ giúp bạn nắm được khái niệm overloading là gì. Bên cạnh đó nó còn giúp bạn đừng bị nhầm lẫn giữa overridingoverloading nữa đấy.

Nạp Chồng Phương Thức (Overloading) Là Gì?

Đây là một khái niệm khá hay trong OOP. Overloading cho phép một lớp có khả năng định nghĩa ra nhiều phương thức có cùng tên, nhưng khác nhau về tham số truyền vào. Phương thức ở đây có bao gồm luôn constructor nhé.

Kỹ thuật overloading làm tăng tính sử dụng cho các phương thức bên trong một lớp.

Bạn thử nhìn vào ví dụ sau đây.

public class HinhTron extends HinhHoc {
	    
    // Không tham số truyền vào,
    // ứng dụng phải gọi các phương thức nhập bán kính và đơn vị từ console
    public void nhapBanKinh() {
    }
    
    // Có một tham số truyền vào là bán kính,
    // ứng dụng phải gọi thêm phương thức nhập đơn vị từ console
    public void nhapBanKinh(float banKinh) {
    }
    
    // Có hai tham số truyền vào là bán kính và đơn vị,
    // ứng dụng chỉ việc gán hai giá trị này vào các thuộc tính tương ứng
    public void nhapBanKinh(float banKinh, int donVi) {
    }
 
}

Như mình có nói trên kia rằng, overloading cho phép bạn khai báo nhiều phương thức trong một lớp có tên trùng nhau, nhưng khác tham số như các phương thức nhapBanKinh ở ví dụ trên.

Bạn cũng đã thấy, rõ ràng overridingoverloading dễ nhầm lẫn nhau ở chỗ, rằng chúng đều nói đến sự đặt tên trùng nhau của các phương thức. Nhưng overriding bắt buộc bạn phải đặt tên phương thức của lớp con trùng với tên phương thức của lớp cha, đồng thời các tham số truyền vào cũng phải trùng nhau. Còn overloading lại bắt buộc đặt tên các phương thức trong một lớp trùng nhau (không liên quan gì đến lớp cha cả), và các tham số truyền vào các phương thức này phải khác nhau.

Đau đầu đúng không. Bạn sẽ quen nhanh thôi khi thao tác nhiều với hai kỹ thuật này.

Nạp Chồng Phương Thức Có Tác Dụng Gì?

Theo như mình thấy. Nếu bạn áp dụng overloading vào một lớp, tức xây dựng nhiều phương thức trùng tên trong một lớp. Nó sẽ không có tác dụng ngay cho lớp đó, chẳng hạn như nó không giúp code của lớp đó gọn gàng, hay rõ ràng hơn để bạn dễ code hay dễ quản lý gì đâu, đôi khi nó gây ra một sự xáo trộn nhất định về mặt tổ chức bên trong lớp đó, nếu như bạn có quá nhiều các phương thức trùng tên.

Nhưng overloading lại phát huy tác dụng rất lớn khi bạn gọi đến chúng từ các lớp khác. Nó làm tăng tính sử dụng của lớp có dùng kỹ thuật overloading.

Mình sẽ cho bạn một dẫn chứng, bạn có nhớ rằng, mỗi khi gọi đến phương thức để in dữ liệu ra console, bạn có thấy rất nhiều tùy chọn đến các phương thức cùng tên hay không. Mời bạn cùng xem các gợi ý cho phương thức println như hình sau.

Overloading - Ví dụ nạp chồng

Bạn thấy đó, với việc sử dụng nhiều phương thức cùng tên println nhưng khác tham số như trên, sẽ làm tăng tính hiệu quả sử dụng của System.out. Khi này thì phương thức println đã “bao sô” hết tất cả các tham số có thể có rồi, do đó bạn có thể yêu cầu phương thức này xuất ra console bất cứ dữ liệu nào mà bạn muốn, nó đều làm được mà không ỏng ẹo gì cả đúng không nào.

Quay lại lớp HinhTron mà bạn vừa thử nghiệm trên kia, nếu như ở đâu đó có khai báo và gọi đến các phương thức nhapBanKinh bên trong lớp này, bạn sẽ thấy sự gợi ý đến ba phương thức được overloading như bên dưới. Thật là “chuyên nghiệp”.

Overloading - Ví dụ nạp chồng 2

Thực Hành Xây Dựng Ứng Dụng Tính Lương Cho Nhân Viên

Đã lâu rồi chúng ta không làm cái gì đó cho nó mới mẻ hay hoành tráng. Vậy thì bài hôm nay bạn hãy cố gắng vận động gân cốt một tí. Mời bạn mở Eclipse lên và thử xây dựng project nho nhỏ sau. Bạn hãy đọc yêu cầu rồi cố gắng vận dụng hết tất cả các kiến thức về OOP từ trước tới giờ vô bài thực hành luôn nhé.

Yêu Cầu Chương Trình

Ứng dụng của chúng ta phục vụ một công ty nhỏ. Với một số nguyên tắc sau.

– Công ty này có hai loại nhân viên, đó là nhân viên toàn thời giannhân viên thời vụ.
Nhân viên toàn thời gianlính sẽ hưởng lương 10 củ một tháng. Nhân viên toàn thời giansếp sẽ hưởng lương 20 củ một tháng.
Nhân viên toàn thời gian nếu làm thêm ngày nào thì sẽ được cộng thêm 800k mỗi ngày, bất kể chức vụ.
Nhân viên thời vụ cứ làm mỗi giờ được 100k, không phân biệt chức vụ gì cả. Làm nhiều thì hưởng nhiều.

Vậy thôi, ứng dụng sẽ cho phép nhập vào từng nhân viên. Mỗi nhân viên có tên nhân viên. Có loại nhân viên toàn thời gian hay bán thời gian. Nhân viên toàn thời gian thì là nhân viên lính hay nhân viên sếp, có làm thêm ngày nào không. Nhân viên thời vụ thì làm được mấy giờ. Cuối cùng dựa vào các thông tin đó, sẽ xuất ra màn hình lương tương ứng.

Sơ Đồ Lớp

Do yêu cầu chương trình có phần phức tạp, nên chỉ có thể giải thích rõ ràng nhất thông qua sơ đồ lớp mà thôi.

Overloading - UML

Bạn có thể thấy rằng, sơ đồ này có thêm nhiều thông tin hơn so với những ngày đầu bạn mới làm quen. Để mình giải thích một số thông tin bổ sung này.

– Thông tin package được thể hiện ở dưới tên lớp, với font chữ nhỏ hơn. Như vậy nhìn sơ đồ chúng ta thấy các lớp NhanVien, NhanVienFullTimeNhanVienPartTime nằm trong cùng package model. Lớp Configs nằm trong package util.
Khả năng truy cập của các thuộc tính và phương thức nay rõ ràng hơn. Khi đó dấu (-)private, dấu (+)public, và dấu (#)protected.
Hằng số là các giá trị được viết in hoa.
– Còn giá trị static sẽ được gạch chân, như các thuộc tính trong lớp Configs.

Như vậy sơ đồ của chúng ta ngày càng rõ ràng hơn rồi đó.

Xây Dựng Các Lớp

Đến đây bạn tự code được rồi đó.

Đầu tiên là lớp Configs để lưu các giá trị tĩnh, như mức lương tháng, lương ngày, lương giờ,…

package util;

public class Configs {

    // Loại nhân viên
    public static final int NHAN_VIEN_SEP = 1;
    public static final int NHAN_VIEN_LINH = 2;
    
    // Lương nhân viên
    public static final long LUONG_NHAN_VIEN_FULL_TIME_SEP = 20000000; // Lương tháng của sếp
    public static final long LUONG_NHAN_VIEN_FULL_TIME_LINH = 10000000; // Lương tháng của lính
    public static final long LUONG_LAM_THEM_MOI_NGAY = 800000; // Làm thêm mỗi ngày của nhân viên toàn thời gian được 800 k
    public static final long LUONG_NHAN_VIEN_PART_TIME_MOI_GIO = 100000; // Lương nhân viên thời vụ mỗi giờ 100 k
}

Kế đến là lớp cha NhanVien.

package model;

public class NhanVien {

    protected String ten;
    protected long luong;
    
    public NhanVien() {
        
    }
    
    public NhanVien(String ten) {
        this.ten = ten;
    }
    
    protected String loaiNhanVien() {
        // Lớp con phải override để lo vụ loại nhân viên này
        return "";
    }
    
    public void xuatThongTin() {
        System.out.println("===== Nhân viên: " + ten + " =====");
        System.out.println("- Loại nhân viên: " + loaiNhanVien());
        System.out.println("- Lương: " + luong + " VND");
    }
}

Rồi đến hai lớp con NhanVienFullTimeNhanVienPartTime.

package model;

import util.Configs;

/**
 * NhanVienFullTime chính là nhân viên toàn thời gian
 */
public class NhanVienFullTime extends NhanVien {
    
    private int ngayLamThem; // Ngày làm thêm của nhân viên
    private int loaiChucVu; // Chức vụ là lính hay sếp
    
    public NhanVienFullTime(String ten) {
        super(ten);
        this.loaiChucVu = Configs.NHAN_VIEN_LINH; // Mặc định là lính
    }
    
    public NhanVienFullTime(String ten, int ngayLamThem) {
        super(ten);
        this.ngayLamThem = ngayLamThem;
        this.loaiChucVu = Configs.NHAN_VIEN_LINH; // Mặc định là lính
    }
    
    public void setLoaiChucVu(int loaiChucVu) {
        this.loaiChucVu = loaiChucVu;
    }
    
    @Override
    public String loaiNhanVien() {
        if (loaiChucVu == Configs.NHAN_VIEN_LINH) {
            return "Lính toàn thời gian" + (ngayLamThem > 0 ? " (có làm thêm ngày)":"");
        } else {
            return "Sếp toàn thời gian" + (ngayLamThem > 0 ? " (có làm thêm ngày)":"");
        }
    }
    
    public void tinhLuong() {
        if (loaiChucVu == Configs.NHAN_VIEN_LINH) {
            luong = Configs.LUONG_NHAN_VIEN_FULL_TIME_LINH + ngayLamThem * Configs.LUONG_LAM_THEM_MOI_NGAY;
        } else if (loaiChucVu == Configs.NHAN_VIEN_SEP) {
            luong = Configs.LUONG_NHAN_VIEN_FULL_TIME_SEP + ngayLamThem * Configs.LUONG_LAM_THEM_MOI_NGAY;
        }
    }
}
package model;

import util.Configs;

/**
 * NhanVienPartTime chính là nhân viên thời vụ
 */
public class NhanVienPartTime extends NhanVien {
    
    private int gioLamViec; // Tổng số giờ làm việc của nhân viên
    
    public NhanVienPartTime(String ten, int gioLamViec) {
        this.ten = ten;
        this.gioLamViec = gioLamViec;
    }
    
    @Override
    public String loaiNhanVien() {
        return "Nhân viên thời vụ";
    }
    
    public void tinhLuong() {
        luong = Configs.LUONG_NHAN_VIEN_PART_TIME_MOI_GIO * gioLamViec;
    }
}

Và đây là lời gọi đến từ phương thức main().

package main;

import model.NhanVienFullTime;
import model.NhanVienPartTime;
import util.Configs;

public class MainClass {

    public static void main(String[] args) {
        // Công ty có 3 nhân viên toàn thời gian, trong đó có 1 sếp, sếp không làm thêm ngày nào
        NhanVienFullTime sep = new NhanVienFullTime("Trần Văn Sếp");
        sep.setLoaiChucVu(Configs.NHAN_VIEN_SEP);
        NhanVienFullTime linh1 = new NhanVienFullTime("Nguyễn Văn Lính"); // Không làm thêm ngày nào
        NhanVienFullTime linh2 = new NhanVienFullTime("Lê Thị Lính", 3); // Làm thêm 3 ngày
        
        // Công ty đang thuê 1 nhân viên thời vụ
        NhanVienPartTime thoiVu = new NhanVienPartTime("Phan Thị Thời Vụ", 240); // Cô ấy siêng làm lắm
        
        // Tính lương cho nhân viên
        sep.tinhLuong();
        linh1.tinhLuong();
        linh2.tinhLuong();
        thoiVu.tinhLuong();
        
        // In thông tin nhân viên
        sep.xuatThongTin();
        linh1.xuatThongTin();
        linh2.xuatThongTin();
        thoiVu.xuatThongTin();
    }

}

Đây là kết quả khi bạn thực thi chương trình lên.

Overloading - Kết quả ví dụ

Chúng ta vừa trải qua một kiến thức thú vị nữa của Java, kiến thức về nạp chồng phương thức, hay còn gọi overloading. Qua bài học thì bạn cũng đã nắm rõ và phân biệt tốt thế nào là overriding và thế nào là overloading rồi đúng không nào.

Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy đánh giá 5 sao nếu thấy thích bài viết, hãy comment bên dưới nếu có thắc mắc, hãy để lại địa chỉ email của bạn để nhận được thông báo mới nhất khi có bài viết mới, và nhớ chia sẻ các bài viết của Yellow Code Books đến nhiều người khác nữa nhé.

Bài Kế Tiếp

Chúng ta cùng làm quen với một trong những đặc tính nổi trội khác trong OOP nữa, đó là tính đa hình, hay còn được gọi với một cái tên xa lạ, khó nhớ, và cũng chẳng thân thương gì ráo: Polymorphism.

Advertisements
Rating: 5.0/5. From 7 votes.
Please wait...

One comment

  1. Ở NhanVienPartTime, trong phương thức khởi tạo lại ghi là this.ten = ten nhỉ? Phải là super(ten); mới đúng chứ.

Gửi phản hồi