Menu
CoddyTech

Ghi đè phương thức (thời gian chạy)

Thuộc phần Lập trình hướng đối tượng trong hành trình Java của Coddy. Bài học 29 trên 87.

Trong khi việc nạp chồng phương thức (method overloading) được xác định tại thời điểm biên dịch, ghi đè phương thức (method overriding) thể hiện tính đa hình lúc chạy (run-time polymorphism). Ở đây, Java quyết định phương thức nào sẽ chạy khi chương trình thực sự đang chạy, dựa trên kiểu thực tế của đối tượng thay vì kiểu được khai báo của biến.

Bạn đã học về việc ghi đè phương thức trong chương kế thừa. Điều khiến cách này trở nên đa hình là cách Java xử lý khi một tham chiếu đến lớp cha trỏ đến một đối tượng thuộc lớp con:

public class Animal {
    public void speak() {
        System.out.println("Some sound");
    }
}

public class Dog extends Animal {
    @Override
    public void speak() {
        System.out.println("Bark!");
    }
}

public class Cat extends Animal {
    @Override
    public void speak() {
        System.out.println("Meow!");
    }
}

Bây giờ, hãy quan sát điều gì xảy ra khi ta dùng các tham chiếu kiểu lớp cha:

Animal myPet = new Dog();
myPet.speak();  // Đầu ra: Bark!

myPet = new Cat();
myPet.speak();  // Đầu ra: Meow!

Mặc dù myPet được khai báo là Animal, Java gọi phương thức đã ghi đè dựa trên kiểu thực tế của đối tượng khi chương trình chạy. Đây gọi là cơ chế điều phối phương thức động (dynamic method dispatch). JVM xem đối tượng thực sự thuộc kiểu nào, chứ không dựa vào kiểu mà biến khai báo.

Điều này cho phép bạn dùng các mẫu mạnh mẽ như xử lý đồng nhất nhiều đối tượng khác nhau:

Animal[] pets = {new Dog(), new Cat(), new Dog()};
for (Animal pet : pets) {
    pet.speak();  // Mỗi đối tượng gọi phiên bản riêng của mình
}

Đa hình lúc chạy (run-time polymorphism) giúp bạn viết code linh hoạt, hoạt động với các kiểu cha và tự động dùng hành vi phù hợp của lớp con.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống thông báo để minh họa tính đa hình lúc chạy. Bạn sẽ tạo một hệ thống phân cấp các loại thông báo, trong đó cùng một lời gọi phương thức tạo ra đầu ra khác nhau tùy thuộc vào kiểu đối tượng thực tế lúc chạy.

Bạn sẽ sắp xếp code trong bốn tệp:

  • Notification.java: Tạo lớp cơ sở dùng chung cho tất cả thông báo. Mỗi thông báo có trường recipient (String). Thêm hàm khởi tạo để khởi tạo trường này, một phương thức getter và một phương thức send() để in ra: Sending notification to [recipient]
  • EmailNotification.java: Tạo một lớp kế thừa từ Notification. Thông báo email có thêm trường subject. Dùng super để khởi tạo lớp cha. Ghi đè phương thức send() để in ra: Emailing [recipient]: [subject]
  • SMSNotification.java: Tạo một lớp khác kế thừa từ Notification. Thông báo SMS có thêm trường phoneNumber. Ghi đè send() để in ra: Texting [phoneNumber] for [recipient]
  • Main.java: Đây là lúc tính đa hình lúc chạy phát huy tác dụng! Bạn sẽ nhận ba đầu vào: tên người nhận, tiêu đề email và số điện thoại. Tạo một mảng các tham chiếu Notification chứa ba đối tượng theo thứ tự sau: một Notification cơ sở, một EmailNotification và một SMSNotification (tất cả đều dùng cùng một người nhận). Sau đó, lặp qua mảng và gọi send() trên từng phần tử. Hãy xem cách Java tự động gọi phiên bản được ghi đè phù hợp dựa trên kiểu thực tế của từng đối tượng!

Bạn sẽ nhận ba đầu vào: tên người nhận (String), tiêu đề email (String) và số điện thoại (String).

Đầu ra của bạn cần có ba dòng, mỗi lần gọi send() tạo ra đầu ra khác nhau dù bạn gọi cùng một phương thức trên các tham chiếu Notification. Đó chính là cơ chế điều phối phương thức động!

Tự làm thử

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Đọc các giá trị đầu vào
        String recipient = scanner.nextLine();
        String subject = scanner.nextLine();
        String phoneNumber = scanner.nextLine();
        
        // TODO: Tạo một mảng (array) gồm 3 phần tử là các tham chiếu đến Notification
        // Mảng cần chứa các phần tử theo thứ tự sau:
        // 1. Một đối tượng (object) Notification cơ sở
        // 2. Một đối tượng EmailNotification
        // 3. Một đối tượng SMSNotification
        // Tất cả đều dùng cùng một người nhận
        
        // TODO: Duyệt mảng bằng vòng lặp (loop) và gọi send() trên từng phần tử
        // Đây là ví dụ về tính đa hình khi chạy chương trình!
    }
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Java online