CodeGym /Các khóa học /JAVA 25 SELF /Mối liên hệ giữa tính đa hình và các lớp trừu tượng

Mối liên hệ giữa tính đa hình và các lớp trừu tượng

JAVA 25 SELF
Mức độ , Bài học
Có sẵn

1. Lớp và phương thức trừu tượng

Đôi khi trong cuộc sống (và trong lập trình) ta muốn nói: “Ừ thì, tôi không biết chính xác nó được làm thế nào, nhưng chắc chắn là nó phải tồn tại!” Ví dụ, mọi loài động vật đều phải biết phát ra âm thanh, nhưng cụ thể âm thanh nào — phụ thuộc vào từng loài. Cho những trường hợp như vậy, Java có các lớp trừu tượngcác phương thức trừu tượng.

Lớp trừu tượng là lớp không thể được tạo trực tiếp (không thể viết new Animal() nếu Animal là trừu tượng), nhưng có thể được kế thừa. Lớp như vậy có thể chứa cả các phương thức thông thường (đã được hiện thực) lẫn các phương thức trừu tượng — tức là được khai báo nhưng chưa có hiện thực.

Phương thức trừu tượng là phương thức không có thân. Nó được khai báo bằng từ khóa abstract và bắt buộc phải được hiện thực trong các lớp con (trừ khi lớp con đó cũng là trừu tượng).

Ví dụ thực tế

Giả sử chúng ta có một ứng dụng cho sở thú. Ta muốn mọi con vật đều có phương thức makeSound(), nhưng không biết chính xác chúng kêu ra sao. Khi đó, ta tạo một lớp trừu tượng:

public abstract class Animal {
    public abstract void makeSound(); // Phương thức trừu tượng
}

Và các loài cụ thể sẽ hiện thực phương thức này theo cách của mình:

public class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Gâu gâu!");
    }
}

public class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meo meo!");
    }
}

Bây giờ, nếu ai đó cố gắng tạo new Animal(), trình biên dịch sẽ nói ngay: “Xin lỗi, nhưng động vật trừu tượng thì không tồn tại trong tự nhiên!” Điều này hữu ích: bạn bảo đảm rằng trong chương trình chỉ có các con vật cụ thể với hành vi cụ thể.

2. Đa hình thông qua trừu tượng hóa

Trừu tượng hóa là việc rút ra một giao diện chung cho một nhóm đối tượng. Lớp trừu tượng chính là thứ xác lập giao diện chung đó: nó cho biết những phương thức nào bắt buộc phải được mọi lớp con hiện thực.

Đa hình và trừu tượng hóa hoạt động song hành: lớp trừu tượng bảo đảm rằng mọi lớp con đều có các phương thức cần thiết, còn tính đa hình cho phép gọi các phương thức đó thông qua một tham chiếu kiểu cơ sở.

Ví dụ: tạo một sở thú

Hãy gom lại một sở thú nhỏ. Ta có lớp trừu tượng Animal và một vài lớp con của nó:

public abstract class Animal {
    public abstract void makeSound();
}
public class Cow extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Ò...!");
    }
}
public class Duck extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Quạc quạc!");
    }
}

Bây giờ ta có thể tạo một mảng động vật:

Animal[] zoo = {
    new Dog(),
    new Cat(),
    new Cow(),
    new Duck()
};

for (Animal animal : zoo) {
    animal.makeSound(); // Với mỗi con vật sẽ gọi phương thức "đúng"
}

Mỗi đối tượng trong mảng là một con vật cụ thể, nhưng với mã thì nó chỉ là Animal. Nhờ trừu tượng hóa và tính đa hình, ta có thể yên tâm rằng mọi đối tượng đều có phương thức makeSound() và nó sẽ hoạt động đúng.

3. Dùng lớp trừu tượng để triển khai đa hình

Hãy xét một ví dụ thực tế hơn. Hình dung ta phát triển ứng dụng quản lý nhân viên trong công ty. Có nhiều kiểu nhân viên: quản lý, lập trình viên, kiểm thử viên. Tất cả đều có phương thức chung work(), nhưng cách họ thực hiện thì khác nhau.

Lớp trừu tượng Employee

public abstract class Employee {
    protected String name;

    public Employee(String name) {
        this.name = name;
    }

    public abstract void work();
}

Các lớp con cụ thể

public class Manager extends Employee {
    public Manager(String name) {
        super(name);
    }

    @Override
    public void work() {
        System.out.println(name + " chỉ đạo nhóm.");
    }
}

public class Developer extends Employee {
    public Developer(String name) {
        super(name);
    }

    @Override
    public void work() {
        System.out.println(name + " viết mã.");
    }
}

public class Tester extends Employee {
    public Tester(String name) {
        super(name);
    }

    @Override
    public void work() {
        System.out.println(name + " kiểm thử ứng dụng.");
    }
}

Sử dụng tính đa hình

Bây giờ ta có thể tạo một mảng nhân viên và gọi phương thức work() cho từng người:

Employee[] employees = {
    new Manager("Anna"),
    new Developer("Ivan"),
    new Tester("Maria")
};

for (Employee e : employees) {
    e.work();
}

Kết quả:

Anna chỉ đạo nhóm.
Ivan viết mã.
Maria kiểm thử ứng dụng.

Lưu ý: trong vòng lặp, ta không biết (và cũng không cần biết!) kiểu cụ thể của từng nhân viên. Ta chỉ việc gọi work(), và mỗi đối tượng sẽ làm phần việc của mình.

4. Những lưu ý hữu ích

Bảo đảm hiện thực phương thức
Lớp trừu tượng buộc mọi lớp con phải hiện thực các phương thức cần thiết. Nếu bạn quên hiện thực một phương thức trừu tượng trong lớp con, trình biên dịch sẽ nhắc nhở ngay: “Bạn phải làm điều đó!”.

Giao diện chung
Mã làm việc với mảng hoặc danh sách kiểu trừu tượng (Employee[], List<Animal>) có thể hoàn toàn tổng quát. Bạn có thể thêm các lớp con mới — và không cần sửa đổi mã chính.

Bảo vệ khỏi các đối tượng “lạ”
Vì không thể tạo trực tiếp một lớp trừu tượng, nên sẽ không ai vô tình tạo ra một đối tượng “mơ hồ” không hiện thực các phương thức cần thiết.

Lý thuyết và cú pháp: khai báo lớp và phương thức trừu tượng

  • Lớp trừu tượng được khai báo bằng từ khóa abstract trước class.
  • Phương thức trừu tượng được khai báo bằng từ khóa abstract và không có thân (chỉ có dấu chấm phẩy).
  • Một lớp có ít nhất một phương thức trừu tượng bắt buộc phải là lớp trừu tượng.
  • Lớp kế thừa từ lớp trừu tượng phải hiện thực tất cả các phương thức trừu tượng của nó, hoặc chính lớp đó cũng phải là trừu tượng.

Sơ đồ

public abstract class Animal {
    public abstract void makeSound();
}

public class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meo meo!");
    }
}

5. Những lỗi thường gặp khi làm việc với lớp trừu tượng

Lỗi №1: cố gắng tạo đối tượng của lớp trừu tượng.
Mã như new Animal() sẽ không biên dịch. Lớp trừu tượng giống như hướng dẫn lắp ráp nội thất mà chưa có linh kiện: khi chưa có một lớp con cụ thể, bạn không thể lắp đối tượng.

Lỗi №2: quên hiện thực phương thức trừu tượng trong lớp con.
Nếu bạn khai báo một phương thức trừu tượng nhưng không hiện thực nó trong lớp kế thừa (và cũng không biến lớp đó thành trừu tượng), trình biên dịch sẽ báo lỗi.

Lỗi №3: quên về mức truy cập.
Phương thức ghi đè không thể có mức truy cập chặt chẽ hơn so với lớp cơ sở. Ví dụ, nếu phương thức trừu tượng là public, thì phần hiện thực cũng phải là public (không phải protected hay private).

Lỗi №4: cố dùng phương thức trừu tượng có thân.
abstract — phương thức không thể có thân; nếu không, trình biên dịch sẽ bảo: “Quyết định đi — hoặc trừu tượng, hoặc hiện thực!”.

Lỗi №5: tính đa hình không áp dụng cho phương thức tĩnh.
Tính đa hình chỉ hoạt động với phương thức không tĩnh. Phương thức tĩnh không được ghi đè — chúng bị che khuất, vì vậy hành vi khi gọi phụ thuộc vào kiểu biến, không phụ thuộc vào đối tượng thực tế.

1
Khảo sát/đố vui
, cấp độ , bài học
Không có sẵn
Tính đa hình và quá tải
Tính đa hình và quá tải
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION