Trong Java, một abstract class (lớp trừu tượng) là một lớp không thể được khởi tạo trực tiếp và thường được sử dụng như một lớp cơ sở cho các lớp con kế thừa. Nó cung cấp một khung sườn cho các lớp con, trong đó một số phương thức có thể được triển khai (implemented) trong lớp cha, trong khi các phương thức khác sẽ phải được các lớp con triển khai cụ thể.
Abstract class rất hữu ích khi bạn muốn cung cấp một số phương thức hoặc thuộc tính chung cho nhiều lớp con, nhưng đồng thời, muốn các lớp con có thể cung cấp phần triển khai cụ thể cho các hành vi khác.
1. Đặc điểm của Abstract Class
1.1 Phương thức trừu tượng và phương thức cụ thể
- Phương thức trừu tượng (abstract method) là phương thức không có phần thân (body), chỉ có khai báo, và lớp con phải triển khai cụ thể.
- Phương thức cụ thể (concrete method) là phương thức có phần triển khai cụ thể trong lớp trừu tượng, và các lớp con có thể kế thừa hoặc ghi đè (override).
Ví dụ:
abstract class Animal {
// Phương thức trừu tượng (không có phần thân)
abstract void sound();
// Phương thức cụ thể (có phần thân)
void sleep() {
System.out.println("This animal is sleeping.");
}
}
1.2 Không thể khởi tạo trực tiếp
Một abstract class không thể được tạo đối tượng trực tiếp. Bạn chỉ có thể sử dụng lớp con của abstract class để khởi tạo đối tượng.
Ví dụ:
Animal animal = new Animal(); // Lỗi biên dịch
Thay vào đó, bạn phải tạo một lớp con kế thừa abstract class đó, sau đó khởi tạo lớp con này:
class Dog extends Animal {
void sound() {
System.out.println("Dog barks");
}
}
Dog dog = new Dog(); // Đúng
1.3 Có thể có constructor
Mặc dù không thể khởi tạo trực tiếp, nhưng abstract class vẫn có thể có constructor. Constructor này có thể được các lớp con gọi để khởi tạo các thuộc tính chung.
Ví dụ:
abstract class Animal {
String name;
// Constructor
Animal(String name) {
this.name = name;
}
abstract void sound();
}
class Dog extends Animal {
Dog(String name) {
super(name);
}
void sound() {
System.out.println(name + " barks");
}
}
1.4 Kế thừa đơn (Single Inheritance)
Trong Java, một lớp chỉ có thể kế thừa từ một abstract class duy nhất. Điều này giúp tránh sự phức tạp của kế thừa đa hình mà Java không hỗ trợ đối với các lớp.
Ví dụ:
class Dog extends Animal { // Đúng
}
class Cat extends Animal { // Đúng
}
class Hybrid extends Dog, Cat { // Sai: Java không hỗ trợ kế thừa đa hình với các class
}
2. Khi nào nên sử dụng Abstract Class?
Bạn nên sử dụng abstract class khi:
- Có hành vi chung mà bạn muốn các lớp con kế thừa nhưng cũng muốn buộc chúng phải cung cấp triển khai cụ thể cho một số hành vi khác.
- Có thuộc tính chung (common fields) mà tất cả các lớp con đều sử dụng.
- Có phương thức cụ thể mà bạn muốn lớp con thừa hưởng nhưng không cần phải ghi đè (override).
- Kiểm soát chặt chẽ hơn về cách thức triển khai các phương thức so với khi sử dụng interface.
Ví dụ: Nếu bạn có một lớp cơ sở là Animal và muốn tất cả các động vật đều có thể ngủ, nhưng cách phát ra âm thanh sẽ khác nhau tùy theo từng loại động vật, thì bạn có thể định nghĩa abstract class Animal với một phương thức sleep() cụ thể và một phương thức sound() trừu tượng.
abstract class Animal {
abstract void sound(); // Phương thức trừu tượng (lớp con phải triển khai)
void sleep() {
System.out.println("This animal is sleeping.");
}
}
class Dog extends Animal {
void sound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
void sound() {
System.out.println("Cat meows");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.sound(); // Output: Dog barks
dog.sleep(); // Output: This animal is sleeping.
Cat cat = new Cat();
cat.sound(); // Output: Cat meows
cat.sleep(); // Output: This animal is sleeping.
}
}
3. So sánh với Interface
Điểm tương đồng
- Cả abstract class và interface đều có thể chứa các phương thức trừu tượng mà không có phần thân.
- Đều được sử dụng để tạo ra các hành vi chung mà các lớp con hoặc lớp thực thi phải tuân theo.
Điểm khác biệt
- Abstract class có thể chứa cả phương thức cụ thể lẫn trừu tượng, trong khi interface trước Java 8 chỉ chứa các phương thức trừu tượng (sau Java 8, interface có thể có phương thức mặc định và phương thức tĩnh).
- Abstract class có thể có constructor, còn interface thì không.
- Một lớp có thể kế thừa một abstract class, nhưng có thể implements nhiều interfaces.
4. Kết luận
Abstract class là một công cụ quan trọng trong lập trình hướng đối tượng để cung cấp một lớp cơ sở với cả phương thức trừu tượng và phương thức cụ thể. Nó giúp giảm bớt việc lặp lại mã và cho phép các lớp con tập trung vào việc triển khai các hành vi cụ thể.
Abstract class thường được sử dụng khi bạn có một số hành vi chung mà tất cả các lớp con cần có, và bạn muốn cung cấp phần triển khai chung đó, đồng thời buộc các lớp con triển khai một số hành vi khác một cách cụ thể.