Abstract class không tạo instance trực tiếp được — chỉ làm template cho subclass.
Subclass phải implement các abstract method.
abstract class HttpClient {
protected final String baseUrl;
protected HttpClient(String baseUrl) { this.baseUrl = baseUrl; }
public abstract <T> T get(String path, Class<T> type); // subclass implement
// Template method pattern — khung cố định, subclass điền chi tiết
public final <T> T fetch(String path, Class<T> type) {
log("call");
T result = get(path, type);
log("done");
return result;
}
}Đặc điểm: có constructor, có state (instance field), trộn abstract + concrete method, dùng đủ access modifier. Chỉ 1 parent.
Khi nào chọn abstract class (thay vì interface):
- Subclass cần share state hoặc code chung — interface không có instance field.
- Cần constructor enforce invariant.
- Template method pattern — như fetch() ở trên.
- Cần protected member — interface ngầm public.
Khi không dùng: subclass không liên quan, cần đa kế thừa contract, hoặc không có state chung → dùng interface.
Java 8+ default method làm mờ ranh giới, nhưng abstract class duy nhất giữ được instance state + constructor logic.
Quy tắc: ưu tiên interface; chỉ chọn abstract class khi thực sự cần state chung hoặc constructor.
An abstract class cannot be instantiated directly — it serves as a template for subclasses.
Subclasses must implement abstract methods.
abstract class HttpClient {
protected final String baseUrl;
protected HttpClient(String baseUrl) { this.baseUrl = baseUrl; }
public abstract <T> T get(String path, Class<T> type); // subclass implements
// Template method pattern — fixed skeleton, subclass fills the gap
public final <T> T fetch(String path, Class<T> type) {
log("call");
T result = get(path, type);
log("done");
return result;
}
}Traits: has constructors, has state (instance fields), mixes abstract + concrete methods, all access modifiers available. Only one parent.
When to pick an abstract class (over an interface):
- Subclasses need to share state or implementation — interfaces have no instance fields.
- Need a constructor to enforce invariants.
- Template method pattern — like fetch() above.
- Need protected members — interface members are implicitly public.
When not to: subclasses are unrelated, you need multi-inheritance of contracts, or there is no shared state → use an interface.
Java 8+ default methods blur the line, but abstract classes are uniquely able to hold instance state + constructor logic.
Rule: prefer interfaces; reach for abstract classes only when you truly need shared state or constructor enforcement.