Nạp chồng phương thức - Overloading method
10:44Nạp chồng phương thức - Overloading method
Khái niệm
Tương tự như khái niệm nạp chồng phương thức trong C++,
trong java, nạp chồng phương thức (overloading method) là hiện tượng nhiều
phương thức có cùng tên, tuy nhiên số lượng tham số hoặc kiểu của tham số trong
các phương thức này là khác nhau.
Chú ý: Các phương thức có cùng tên, cùng danh sách tham
số, tuy nhiên, kiểu trả về khác nhau không được xem là hiện tượng
overload.
Tác dụng và cơ chế của overload
Sử dụng overload trong một chương trình nhằm tăng khả
năng đọc của chương trình.
Trình biên dịch sẽ dựa vào tham số được truyền vào phương
thức mà quyết định xem sẽ gọi phương thức nào trong danh sách phương thức
overload.
Overload với tham số kiểu cơ bản
Theo định nghĩa thì chúng ta có 2 cách để tạo ra hiện
tượng overload:
-
Thay đổi số lượng tham số
-
Thay đổi kiểu dữ liệu của tham số
Thay đổi số lượng tham số
Thay đổi kiểu dữ liệu của tham số
Thay đổi số lượng tham số
Đây là trường hợp khá đơn giản và dễ nhận biết nhất của
hiện tượng overload. Các bạn có thể tham khảo đoạn mã nguồn dưới đây:
- public class stdio {
- private String author;
- private static final String address = "stdio.vn";
-
- stdio(){
- author = "Kevin La";
- }
-
- stdio(String author_name){
- author = author_name;
- }
-
- public static void main(String [] args){
- //Call no arguments constructor
- stdio obj = new stdio();
- System.out.println(obj.author);
- //Call constructor has a argument
- stdio obj1 = new stdio("Alice");
- System.out.println(obj1.author);
- }
- }
Trong đoạn mã nguồn trên, tôi đã thực hiện overload
constructor của lớp stdio với 1 constructor không có tham số và 1 constructor có
chứa 1 tham số.
- public class stdio {
- private String author;
- private static final String address = "stdio.vn";
- stdio(){
- author = "Kevin La";
- }
- stdio(String author_name){
- author = author_name;
- }
- public static void main(String [] args){
- //Call no arguments constructor
- stdio obj = new stdio();
- System.out.println(obj.author);
- //Call constructor has a argument
- stdio obj1 = new stdio("Alice");
- System.out.println(obj1.author);
- }
- }
Thay đổi kiểu dữ liệu của tham số
Khi có cùng số lượng tham số, trình biên dịch sẽ dựa vào
kiểu của đối số được truyền vào phương thức, sau đó đối chiếu với kiểu dữ liệu
của tham số của phương thức để chọn ra phương thức thích hợp nhất.
Ví dụ:
- public class Calculation {
- void add(int param1, int param2){
- System.out.println(param1 + param2);
- }
- void add(float param1, float param2){
- System.out.println(param1 + param2);
- }
- public static void main(String [] args){
- Calculation obj = new Calculation();
- obj.add(10, 10);
- obj.add(10.5f, 10.24f);
- }
- }
Tại dòng số 10, trình biên dịch sẽ gọi phương thức add ở
dòng thứ 2 để thực thi do cả 2 tham số truyền vào đều là kiểu int. Giải thích
tương tự với dòng số 11.
Tuy nhiên, ở một khía cạnh khác, tôi muốn đề cập tới hiện
tượng ép kiểu và lớp bao trong java được sử dụng như thế nào trong nạp chồng
phương thức.
Quan sát ví dụ dưới đây:
- public class TestOverload {
- void method(Integer param){
- System.out.println("_Integer invoked");
- }
- void method(long param){
- System.out.println("_long invoked");
- }
- public static void main(String [] args){
- Calculation obj = new Calculation();
- obj.add(10);
- }
- }
Kết quả là:
- _long invoked
Tại dòng số 10, khi truyền 10 là một đối số kiểu int,
trình biên dịch sẽ kiểm tra xem có phương thức add nào có tham số là kiểu int
hay không, tuy nhiên, trình biên dịch không tìm thấy. Thay vào đó, nó tìm thấy 2
phương thức add có tham số là lớp bao của kiểu int là Integer (dòng số 2) và một
kiểu rộng hơn kiểu int là long (dòng số 5). Khi ấy, trình biên dịch sẽ ưu tiên
gọi phương thức có tham số có kiểu rộng hơn đối số truyền vào thay vì phương
thức có tham số là lớp bao của đối số truyền vào.
- public class Calculation {
- void add(int param1, int param2){
- System.out.println(param1 + param2);
- }
- void add(float param1, float param2){
- System.out.println(param1 + param2);
- }
- public static void main(String [] args){
- Calculation obj = new Calculation();
- obj.add(10, 10);
- obj.add(10.5f, 10.24f);
- }
- }
- public class TestOverload {
- void method(Integer param){
- System.out.println("_Integer invoked");
- }
- void method(long param){
- System.out.println("_long invoked");
- }
- public static void main(String [] args){
- Calculation obj = new Calculation();
- obj.add(10);
- }
- }
- _long invoked
Overload và quan hệ kế thừa
Nhắc tới quan hệ kế thừa, chúng ta thường đề cập tới mối
quan hệ IS-A. Nói cách khác, một đối tượng thuộc lớp con cũng là một đối tượng
thuộc lớp cha. Vì thế, khi thực hiện overload phương thức ta hoàn toàn có thể
truyền một đối tượng thuộc lớp con vào một phương thức trong danh sách phương
thức overload có tham số mang kiểu dữ liệu của lớp cha.
Quan sát ví dụ sau đây:
- class Animal{
- }
-
- class Dog extends Animal{
- }
- public class Test_Animal {
- void eat(Animal animal){
- System.out.println("Animal eats everything");
- }
-
- void eat(Dog dog){
- System.out.println("Dogs eat meat");
- }
-
- public static void main(String [] args){
- Test_Animal obj = new Test_Animal();
- Animal animal = new Animal();
- Dog dog = new Dog();
- Animal animal_obj = new Dog();
-
- obj.eat(animal);
- obj.eat(dog);
- obj.eat(animal_obj);
- }
-
- }
Kết quả:
- Animal eats everything
- Dogs eat meat
- Animal eats everything
Kết quả của 2 dòng đầu tiên hoàn toàn dễ hiểu. Tuy nhiên,
kết quả của dòng thứ 3 có thể gây lăn tăn cho bạn đọc. Tôi sẽ giải thích điều
này như sau:
Hãy quan sát dòng code số 19 và 23.
-
Tại dòng số 19, ta khởi tạo một đối tượng lớp Dog và gán
cho một tham chiếu kiểu Animal (hãy nhớ rằng lớp Dog kế thừa từ Animal). Nói
cách khác, ta sử dụng một tham chiếu kiểu Animal tham chiếu tới một đối tượng
kiểu Dog.
-
Như vậy, tại dòng 23, khi gọi phương thức eat() theo
logic thông thường, thì phương thức eat có tham số kiểu Dog sẽ được gọi, nhưng ở
đây, trình biên dịch lại gọi phương thức eat có tham số kiểu Animal. Đó là do,
tham chiếu animal_obj chỉ xác định được đối tượng nó tham chiếu thực sự tới là
đối tượng nào tại quá trình runtime, điều đó đồng nghĩa với, trong khi biên dịch
(compile) animal_obj chưa hề biết đối tượng nó thực sự tham chiếu tới trên heap
và trình biên dịch chỉ biết về animal_obj có kiểu dữ liệu là Animal. Trong khi
đó, việc quyết định chọn phương thức trong danh sách phương thức overload lại
được trình biên dịch quyết định trong quá trình compile. Vì thế, trình biên dịch
sẽ quyết định phương thức overload được gọi dựa vào kiểu dữ liệu của tham chiếu
(Animal), không phải dựa vào kiểu dữ liệu của đối tượng được tham chiếu trỏ tới
(Dog).
Quan sát ví dụ sau đây:
- class Animal{
- }
- class Dog extends Animal{
- }
- public class Test_Animal {
- void eat(Animal animal){
- System.out.println("Animal eats everything");
- }
- void eat(Dog dog){
- System.out.println("Dogs eat meat");
- }
- public static void main(String [] args){
- Test_Animal obj = new Test_Animal();
- Animal animal = new Animal();
- Dog dog = new Dog();
- Animal animal_obj = new Dog();
- obj.eat(animal);
- obj.eat(dog);
- obj.eat(animal_obj);
- }
- }
- Animal eats everything
- Dogs eat meat
- Animal eats everything
Tại dòng số 19, ta khởi tạo một đối tượng lớp Dog và gán
cho một tham chiếu kiểu Animal (hãy nhớ rằng lớp Dog kế thừa từ Animal). Nói
cách khác, ta sử dụng một tham chiếu kiểu Animal tham chiếu tới một đối tượng
kiểu Dog.
Như vậy, tại dòng 23, khi gọi phương thức eat() theo
logic thông thường, thì phương thức eat có tham số kiểu Dog sẽ được gọi, nhưng ở
đây, trình biên dịch lại gọi phương thức eat có tham số kiểu Animal. Đó là do,
tham chiếu animal_obj chỉ xác định được đối tượng nó tham chiếu thực sự tới là
đối tượng nào tại quá trình runtime, điều đó đồng nghĩa với, trong khi biên dịch
(compile) animal_obj chưa hề biết đối tượng nó thực sự tham chiếu tới trên heap
và trình biên dịch chỉ biết về animal_obj có kiểu dữ liệu là Animal. Trong khi
đó, việc quyết định chọn phương thức trong danh sách phương thức overload lại
được trình biên dịch quyết định trong quá trình compile. Vì thế, trình biên dịch
sẽ quyết định phương thức overload được gọi dựa vào kiểu dữ liệu của tham chiếu
(Animal), không phải dựa vào kiểu dữ liệu của đối tượng được tham chiếu trỏ tới
(Dog).
0 nhận xét