Interface hay abstract class
Nội dung bài · 5 mục
- 1.Khái niệm
- 2.Ví dụ
- 3.Thử ngay
- 4.Lỗi hay gặp
- 5.Tóm tắt
Không thể tạo object trực tiếp từ interface hay abstract class, và cả hai đều bắt class khác viết method. Vậy khi nào dùng cái nào? Bài này đưa ra cách chọn nhanh bằng một câu hỏi.
Khái niệm
🆚 Interface và abstract class: interface nói class làm được gì, còn abstract class là class cha chứa sẵn phần code chung cho các class con.
| Interface | Abstract class | |
|---|---|---|
| Chứa code dùng chung | thường không | có |
| Có field thường, constructor | không | có |
| Một class dùng được mấy cái | nhiều | chỉ một |
| Diễn tả quan hệ | "làm được" | "là một" |
Câu hỏi để chọn: các class có code chung cần viết một lần không? Có thì dùng abstract class. Chỉ cần cam kết một khả năng thì dùng interface.
Ví dụ
// Main.java
void main() {
var card = new CardPayment(120000);
System.out.println(card.receipt());
card.refund();
}
// Payment.java
abstract class Payment {
private long amount;
public Payment(long amount) {
this.amount = amount;
}
public long getAmount() {
return amount;
}
public abstract String kind();
public String receipt() {
return kind() + " - " + amount + "đ";
}
}
// Refundable.java
interface Refundable {
void refund();
}
// CashPayment.java
class CashPayment extends Payment {
public CashPayment(long amount) {
super(amount);
}
@Override
public String kind() {
return "Tiền mặt";
}
}
// CardPayment.java
class CardPayment extends Payment
implements Refundable {
public CardPayment(long amount) {
super(amount);
}
@Override
public String kind() {
return "Quẹt thẻ";
}
@Override
public void refund() {
System.out.println(
"Hoàn " + getAmount() + "đ về thẻ");
}
}Paymentlà abstract class vì mọi hình thức thanh toán dùng chungamountvàreceipt().Refundablelà interface vì hoàn tiền chỉ là một khả năng. Thẻ làm được, tiền mặt thì không.CardPayment extends Payment implements Refundable: kế thừa một class cha, đồng thời implement một interface.extendsphải đứng trướcimplements.
Thử ngay
Giữ các file của ví dụ. Tạo thêm file WalletPayment.java dưới đây, rồi
thay main trong Main.java bằng đoạn gọi mới:
// WalletPayment.java
class WalletPayment extends Payment
implements Refundable {
public WalletPayment(long amount) {
super(amount);
}
@Override
public String kind() {
return "Ví điện tử";
}
@Override
public void refund() {
System.out.println(
"Hoàn " + getAmount() + "đ về ví");
}
}// Main.java
void main() {
List<Refundable> refunds = List.of(
new CardPayment(120000),
new WalletPayment(80000));
for (Refundable r : refunds) {
r.refund();
}
}Đoán trước khi chạy: chương trình in ra gì? Thêm new CashPayment(50000)
vào list refunds thì có được không?
Xem kết quả
Hoàn 120000đ về thẻ
Hoàn 80000đ về víThêm CashPayment thì không được, đó là lỗi compile. CashPayment không
implement Refundable, nên không thêm vào List<Refundable> được.
Compiler chặn luôn việc hoàn tiền mặt.
Lỗi hay gặp
Dùng abstract class cho một khả năng. Class đã có cha thì không kế thừa thêm class thứ hai được.
// SAI — lỗi compile: chỉ được một class cha
// Main.java
void main() {
new BankPayment(50000);
}
// BankPayment.java
class BankPayment extends Payment, RefundableBase {
public BankPayment(long amount) {
super(amount);
}
@Override
public String kind() {
return "Chuyển khoản";
}
}
// RefundableBase.java
abstract class RefundableBase {
public abstract void refund();
}// ĐÚNG — khả năng thì dùng interface
// BankPayment.java
class BankPayment extends Payment
implements Refundable {
public BankPayment(long amount) {
super(amount);
}
@Override
public String kind() {
return "Chuyển khoản";
}
@Override
public void refund() {
System.out.println(
"Hoàn " + getAmount() + "đ về tài khoản");
}
}Dùng interface khi có code chung. Mỗi class phải tự chép lại cùng một đoạn code.
// SAI — receipt() bị chép giống hệt ở mọi class
// Bill.java
interface Bill {
String receipt();
}
// CashBill.java
class CashBill implements Bill {
private long amount;
public CashBill(long amount) {
this.amount = amount;
}
@Override
public String receipt() {
return "Tiền mặt - " + amount + "đ";
}
}// ĐÚNG — phần chung viết một lần ở class cha
// Bill.java
abstract class Bill {
private long amount;
public Bill(long amount) {
this.amount = amount;
}
public abstract String kind();
public String receipt() {
return kind() + " - " + amount + "đ";
}
}
// CashBill.java
class CashBill extends Bill {
public CashBill(long amount) {
super(amount);
}
@Override
public String kind() {
return "Tiền mặt";
}
}Tóm tắt
- Có code chung, quan hệ "là một": dùng abstract class.
- Chỉ cần cam kết một khả năng "làm được": dùng interface.
- Một class kế thừa một class cha, nhưng implement được nhiều interface.
- Hai thứ dùng cùng nhau được:
class CardPayment extends Payment implements Refundable.
Tự kiểm tra
0/3 câuClass Truck đã kế thừa Vehicle. Giờ cần thêm khả năng "theo dõi GPS" cho Truck và cả Phone. Nên dùng gì?
Các loại báo cáo đều có chung phần header, footer và cách in. Chỉ phần thân là khác nhau. Nên dùng gì?
Khai báo nào hợp lệ, biết Animal là class còn Swimmable và Flyable là interface?