Menu

Lớp trừu tượng trong Java: giải thích từ khóa abstract

Lớp trừu tượng trong Java là gì, cách khai báo phương thức trừu tượng, vì sao không thể tạo đối tượng từ nó, và khi nào nên chọn lớp trừu tượng thay vì interface.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Lớp trừu tượng là gì

Interface khai báo hành vi mà không có trạng thái. Một lớp (class) thông thường được cài đặt đầy đủ và có thể tạo đối tượng. Lớp trừu tượng (abstract class) nằm giữa hai thứ đó: nó có thể chứa trường, hàm khởi tạo và phương thức hoàn chỉnh như một lớp bình thường, nhưng cũng có thể để một số phương thức chưa cài đặt và không cho tạo đối tượng trực tiếp. Bạn đánh dấu nó bằng từ khóa abstract.

Ý tưởng là gom mọi thứ các lớp con có chung vào một chỗ, đồng thời buộc mỗi lớp con tự viết những phần thực sự khác nhau.

Animal định nghĩa getName() một lần cho mọi lớp con và khai báo sound() là abstract: một phương thức có chữ ký nhưng không có thân hàm, mà Dog phải cung cấp.

Không thể tạo đối tượng từ lớp trừu tượng

Vì lớp trừu tượng có thể có phương thức chưa hoàn thiện, tạo trực tiếp một đối tượng từ nó sẽ cho ra một đối tượng không hoàn chỉnh. Trình biên dịch từ chối việc này:

Animal a = new Animal("???");   // error: Animal is abstract; cannot be instantiated

Bạn luôn tạo đối tượng từ một lớp con cụ thể, tức lớp đã cài đặt mọi phương thức trừu tượng. Sau đó đối tượng của lớp con đó có thể được lưu trong một biến có kiểu là lớp trừu tượng, và đó chính là cách bạn dùng sự trừu tượng hóa.

Phương thức trừu tượng buộc lớp con phải quyết định

Một phương thức abstract là lời hứa mà lớp con phải giữ. Nếu lớp con quên cài đặt một phương thức như vậy, chính lớp con đó trở thành lớp trừu tượng và trình biên dịch sẽ báo cho bạn. Đây là đòn bẩy chính của lớp trừu tượng: nó đảm bảo một hành vi nhất định tồn tại mà không áp đặt hành vi đó làm gì.

describe() chỉ được viết một lần trong Shape nhưng gọi đúng area() riêng của từng lớp con. Lớp trừu tượng cung cấp phần khung chung; các lớp con cung cấp phần chi tiết.

Trạng thái dùng chung và hàm khởi tạo

Khác với interface truyền thống, lớp trừu tượng có thể chứa trường instance và định nghĩa hàm khởi tạo. Hàm khởi tạo không bao giờ tự tạo ra một Animal hay Shape: nó chạy thông qua super(...) khi một lớp con được tạo, để khởi tạo trạng thái dùng chung.

Trường balance, deposit và applyInterest nằm ở một chỗ. Chỉ có quy tắc thực sự thay đổi, tức interestRate(), được để trừu tượng. Hàm khởi tạo của lớp con phải gọi super(...) để khởi tạo trạng thái được kế thừa đó.

Một cái bẫy: kết hợp abstract và final

abstract và final trái ngược nhau. Phương thức abstract đòi hỏi phải được ghi đè; phương thức final cấm ghi đè. Đánh dấu cùng một thứ theo cả hai cách, hoặc tạo một lớp trừu tượng final, đều là lỗi biên dịch. Cũng nhớ rằng lớp trừu tượng có thể có không phương thức trừu tượng nào: khai báo một lớp abstract chỉ để ngăn tạo đối tượng từ nó là hợp lệ, và đôi khi hữu ích cho các kiểu cơ sở mà bạn chỉ muốn kế thừa.

abstract final class Bad { }        // error: abstract and final conflict

abstract class Base {
    abstract final void f();        // error: an abstract method can't be final
}

Lớp trừu tượng và interface

Hai thứ này có phần chồng lấn, nên lựa chọn phụ thuộc vào thứ bạn cần chia sẻ:

  • Lớp trừu tượng: dùng cho các lớp có quan hệ gần gũi cùng chia sẻ trạng thái và code. Savings và Checking đều kế thừa Account, nhận trường balance và logic deposit. Một lớp chỉ kế thừa được một lớp.
  • Interface: dùng cho một khả năng mà các lớp không liên quan có thể cùng có. Bird và Airplane đều có thể là Flyable mà không chia sẻ chút cài đặt nào. Một lớp có thể cài đặt nhiều interface.

Một mẫu thường gặp kết hợp cả hai: interface định nghĩa hợp đồng, còn lớp trừu tượng cài đặt phần code lặp lại để các lớp con cụ thể chỉ phải viết phần riêng của mình.

Tiếp theo: đa hình

Để ý rằng trong mọi ví dụ trên, chúng ta lưu đối tượng của lớp con trong một biến có kiểu là lớp trừu tượng, rồi gọi một phương thức và tự động nhận được hành vi của lớp con. Khả năng đó, một kiểu tham chiếu nhưng nhiều hành vi lúc chạy, chính là đa hình (polymorphism), và nó là thứ khiến lớp trừu tượng và interface phát huy giá trị. Đó là nội dung của trang tiếp theo.

Câu hỏi thường gặp

Lớp trừu tượng trong Java là gì?

Lớp trừu tượng (abstract class) là lớp được khai báo với từ khóa abstract và không thể tự tạo đối tượng. Nó được viết ra để các lớp khác kế thừa. Nó có thể kết hợp các phương thức và trường đã cài đặt đầy đủ (trạng thái và code dùng chung cho lớp con) với các phương thức abstract không có thân hàm, mà mỗi lớp con có trách nhiệm cài đặt.

Có thể tạo đối tượng từ lớp trừu tượng trong Java không?

Không. new AbstractType() là lỗi biên dịch vì lớp trừu tượng có thể có các phương thức chưa cài đặt (abstract), nên đối tượng sẽ không hoàn chỉnh. Bạn tạo đối tượng từ một lớp con cụ thể đã cài đặt mọi phương thức trừu tượng, rồi lưu nó vào biến có kiểu là lớp trừu tượng.

Lớp trừu tượng và interface trong Java khác nhau thế nào?

Lớp trừu tượng có thể có trường instance, hàm khởi tạo và logic cài đặt một phần, nhưng một lớp chỉ kế thừa được một lớp. Interface khai báo hành vi mà không có trạng thái instance, và một lớp có thể cài đặt nhiều interface. Dùng lớp trừu tượng để chia sẻ trạng thái và code giữa các lớp con có quan hệ gần gũi; dùng interface để cho các lớp không liên quan một khả năng chung.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu