VieTopik
Tiếng HànTiếng Anh.NET (C#)Java
  • Góc học tập
Tải app
  • Thư viện
  • Luyện thi
  • Cẩm nang
  • Góc học tập
OOP và thiết kế (Java)Interface và composition
Bài 6/12
5 phút

Interface hay abstract class

Nội dung bài · 5 mục
  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 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ẻ");
    }
}
  • Payment là abstract class vì mọi hình thức thanh toán dùng chung amount và receipt().
  • Refundable là 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. extends phải đứng trước implements.
<<abstract>> Payment amount kind() receipt() <<interface>> Refundable refund() CashPayment CardPayment
Payment là class cha, Refundable là khả năng thêm

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âu
Câu 1

Class 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âu 2

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ì?

Câu 3

Khai báo nào hợp lệ, biết Animal là class còn Swimmable và Flyable là interface?

InterfaceComposition

Nội dung bài

  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt