Chuyển đến nội dung chính
Mô-đun 3 · Bài học 2 trong tổng số 26/14 trong khóa học~15 min
Bài học theo mô-đun (2/2)

Đa hình và các lớp trừu tượng

Đa hình và các lớp trừu tượng là các khái niệm lập trình hướng đối tượng nâng cao cho phép chúng ta viết mã linh hoạt, có khả năng mở rộng độc lập với các triển khai cụ thể.

Đa hình

Thuật ngữ đa hình (nhiều dạng) đề cập đến khả năng xử lý các đối tượng thuộc các lớp khác nhau như thể chúng thuộc về một loại chung. Trong Java, biến tham chiếu siêu lớp có thể lưu trữ tham chiếu đến một đối tượng của bất kỳ lớp con nào của nó.

Java
class Animal {
    public void makeSound() {
        System.out.println("Verso...");
    }
}

class Dog extends Animal {
    public void makeSound() {
        System.out.println("Woof");
    }
}

class Cat extends Animal {
    public void makeSound() {
        System.out.println("Miao");
    }
}

Nhờ tính đa hình, chúng ta có thể làm được điều này:

Java
Animal myAnimal1 = new Dog(); // Polymorphism
Animal myAnimal2 = new Cat(); // Polymorphism

myAnimal1.makeSound(); // Executes Dog's method (Woof)
myAnimal2.makeSound(); // Executes Cat's method (Miao)

Quyết định về phương thức nào sẽ được gọi được đưa ra trong thời gian chạy (Liên kết muộn hoặc gửi động) dựa trên đối tượng thực tế chứ không phải loại biến tham chiếu.

Các lớp trừu tượng (abstract)

Lớp trừu tượng là lớp được đánh dấu bằng từ khóa abstract không thể khởi tạo trực tiếp (bạn không thể thực hiện new MyAbstractClass()). Nó phục vụ như một "mẫu một phần" cho các lớp khác.

Phương pháp trừu tượng

Một lớp trừu tượng có thể chứa các phương thức trừu tượng: các phương thức được khai báo không có phần thân (không có dấu ngoặc nhọn và không có mã), kết thúc bằng dấu chấm phẩy. Các lớp con không trừu tượng được yêu cầu để triển khai tất cả các phương thức trừu tượng được kế thừa.

Java
abstract class Shape {
    String color;

    // Abstract method (no body)
    public abstract double getArea();
}

class Circle extends Shape {
    double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    // Required to implement getArea
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

Liên kết động / Liên kết muộn

Làm thế nào để Java biết phương thức nào sẽ thực thi khi chạy? Quá trình này được gọi là Liên kết muộn (hoặc Liên kết động). Không giống như tình trạng quá tải (được giải quyết tại thời điểm biên dịch), trình biên dịch không biết phương thức cụ thể nào sẽ được thực thi. Thay vào đó, nó tạo ra một lệnh gọi chung. Trong thời gian chạy, Máy ảo Java (JVM) kiểm tra đối tượng thực tế trong bộ nhớ và gọi cách triển khai phương thức được xác định trong lớp của đối tượng đó.

Các lớp trừu tượng và việc triển khai còn thiếu

Nếu một lớp con kế thừa từ một lớp trừu tượng, nó có hai lựa chọn:

  1. Thực hiện tất cả các phương thức trừu tượng: trong trường hợp này, lớp con có thể là một lớp (cụ thể) bình thường và có thể được khởi tạo.
  2. Khai báo chính nó là trừu tượng: nếu lớp con không cung cấp cách triển khai cho tất cả các phương thức trừu tượng được kế thừa thì nó cũng phải được khai báo bằng từ khóa abstract.
Java
abstract class Animal {
    public abstract void makeSound();
}

// This class MUST be abstract because it does not implement makeSound()
abstract class Canine extends Animal {
    // Inherits makeSound() but does not implement it
}

Hãy tự mình thử

tập thể dục#java.m3.l2.e1
Nỗ lực: 0Đang tải…

Khai báo một biến s kiểu Shape và gán một đối tượng Circle mới cho nó bằng cách sử dụng đa hình.

Đang tải trình chỉnh sửa…
Hiển thị gợi ý

Viết `Shape s = new Circle();` để sử dụng loại cơ sở làm loại tham chiếu.

Giải pháp khả dụng sau 3 lần thử

tập thể dục#java.m3.l2.e2
Nỗ lực: 0Đang tải…

Làm cho lớp Shape trở nên trừu tượng và thêm phương thức trừu tượng double getArea(). Sau đó, hoàn thiện Square để nó mở rộng Hình dạng và triển khai cạnh * bên getArea().

Đang tải trình chỉnh sửa…
Hiển thị gợi ý

Khai báo `public abstract double getArea();` trong Shape. Trong Square, thêm `@Override public double getArea() { return side * side; }`.

Giải pháp khả dụng sau 3 lần thử

tập thể dục#java.m3.l2.e3
Nỗ lực: 0Đang tải…

Khai báo trong phương thức chính một mảng kiểu Shape[] chứa đối tượng Hình tròn có bán kính 2,0 và đối tượng Hình vuông có cạnh 3,0. Sau đó, sử dụng vòng lặp (for hoặc for-each) để in diện tích của từng hình bằng cách gọi phương thức getArea().

Đang tải trình chỉnh sửa…
Hiển thị gợi ý

Khai báo mảng bằng `Shape[] Shapes = { new Circle(2.0), new Square(3.0) };` và lặp qua nó bằng cách sử dụng `for (Shape s :shapes)` in `s.getArea()`.

Giải pháp khả dụng sau 3 lần thử