99 Ngày Java — Ngày 17: Interface
Ngày 16 khép lại ở một giới hạn cốt lõi của abstract class: mỗi lớp chỉ có một lớp cha. Nhưng một LapTrinhVien có thể vừa làm thêm giờ, vừa đào tạo người mới, vừa tham gia phỏng vấn — các năng lực độc lập nhau và không tạo thành một cây kế thừa hợp lý. Java mô hình hóa điều này bằng interface: hợp đồng thuần túy, không trạng thái, và một lớp được phép cam kết bao nhiêu hợp đồng tùy ý.

Interface — hợp đồng thuần túy
public interface CoTheThanhToan { // interface: một hợp đồng thuần túy
double tinhPhi(); // ngầm định public abstract —
} // không thân hàm, không trạng thái
public class ThanhToanTheTinDung implements CoTheThanhToan {
@Override
public double tinhPhi() { return 25_000; } // implements = ký hợp đồng:
} // bắt buộc cài đặt đủ method
CoTheThanhToan kenh = new ThanhToanTheTinDung();
System.out.println(kenh.tinhPhi());
// Đa hình qua interface — cơ chế dispatch của Ngày 15// hoạt động y nguyên, không cần một lớp cha chungMọi method của interface ngầm định là
public abstract— không cần và không nên viết lại hai từ khóa này; field khai báo trong interface ngầm định là hằng sốpublic static final.implementslà một cam kết được compiler giám sát: thiếu bất kỳ method nào của hợp đồng, lớp không biên dịch được — đúng cơ chế ràng buộc đã gặp ở abstract method (Ngày 16).Đa hình của Ngày 15 áp dụng nguyên vẹn: biến kiểu interface cầm được mọi đối tượng đã ký hợp đồng, và dynamic dispatch chọn đúng bản cài đặt lúc runtime.
Implement nhiều interface
public class LapTrinhVien extends NhanVien // vẫn chỉ MỘT lớp cha (Ngày 16)
implements CoTheLamThem, CoTheDaoTao { // ...nhưng NHIỀU hợp đồng
@Override public double tinhPhiLamThem() { ... }
@Override public void daoTao(NhanVien nv) { ... }
}
// Các lớp KHÔNG cùng cây kế thừa vẫn ký được cùng một hợp đồng:public class DoiTacNgoai implements CoTheDaoTao { ... } // không extends NhanVien
// Code nghiệp vụ chỉ phụ thuộc hợp đồng — không quan tâm cây kế thừa:static void toChucKhoaHoc(CoTheDaoTao giangVien) { ... } // nhận cả hai loại trênĐây là điểm interface vượt qua giới hạn của kế thừa:
extendschỉ một lớp cha, nhưngimplementsnhiều hợp đồng cùng lúc — vì interface không mang trạng thái nên không phát sinh xung đột dữ liệu.Giá trị lớn nhất nằm ở việc các lớp không cùng cây kế thừa vẫn dùng chung được một hợp đồng —
toChucKhoaHocphục vụ cả nhân viên nội bộ lẫn đối tác ngoài mà không ép hai lớp đó về chung một gốc.Đối chiếu với Ngày 16: cần chia sẻ code và trạng thái trong một họ lớp → abstract class; cần mô tả năng lực cho các lớp bất kỳ → interface. Hai công cụ bổ trợ nhau, không thay thế nhau.
Default method & static method
public interface CoTheThanhToan {
double tinhPhi();
default String moTaPhi() { // default method (Java 8+): có thân hàm,
return "Phí: " + tinhPhi(); // mọi lớp implement thừa hưởng sẵn
}
static CoTheThanhToan macDinh() { // static method: tiện ích gắn với interface
return new ThanhToanTheTinDung();
}
}
// Lý do default method tồn tại: bổ sung method vào interface// đã có hàng trăm lớp implement mà KHÔNG phá vỡ lớp nào.//// Nếu hai interface cung cấp hai default method trùng chữ ký,// lớp implement cả hai BẮT BUỘC override để hóa giải xung đột —// compiler không cho phép sự mơ hồ tồn tại.Default method ra đời (Java 8) để giải bài toán tiến hóa API: bổ sung method cho interface đang được sử dụng rộng rãi mà không phá vỡ mã hiện có — chính là cách
List.sorthayCollection.streamđược thêm vào JDK.Ranh giới cần giữ: default method dành cho hành vi mặc định suy ra từ hợp đồng, không phải nơi chứa logic nghiệp vụ — interface nhiều default method phức tạp là dấu hiệu nên chuyển sang abstract class.
Xung đột hai default method trùng chữ ký được compiler phát hiện và buộc lớp implement tự quyết định — có thể gọi bản cụ thể qua cú pháp
TenInterface.super.method().
Functional interface — cửa ngõ sang lập trình hàm
@FunctionalInterface // đúng MỘT abstract methodpublic interface BoLocNhanVien {
boolean kiemTra(NhanVien nv);
}
// Cách viết quen thuộc đến hôm nay — anonymous class:BoLocNhanVien locLuongCao = new BoLocNhanVien() {
@Override
public boolean kiemTra(NhanVien nv) {
return nv.tinhLuong() > 20_000_000;
}
};
// Java 8+ — lambda: cùng ngữ nghĩa, cú pháp tối giản (chi tiết ở Ngày 51):BoLocNhanVien locLuongCao2 = nv -> nv.tinhLuong() > 20_000_000;
// JDK cung cấp sẵn cả họ functional interface:// Runnable, Comparator<T>, Predicate<T>, Function<T,R>...// — nền tảng của lập trình hàm ở Giai đoạn 6Interface có đúng một abstract method gọi là functional interface —
@FunctionalInterfaceđể compiler kiểm chứng ràng buộc đó (thêm method thứ hai là lỗi biên dịch).Vì chỉ có một method cần cài đặt, một biểu thức lambda đủ để thay thế cả khối anonymous class — đây là nền tảng của Stream API và lập trình hàm, được dành trọn Giai đoạn 6 (Ngày 51–60).
Gặp lại người quen:
Runnable,Comparator— các hợp đồng một method đã tồn tại từ những phiên bản Java đầu tiên, nay trở thành functional interface một cách tự nhiên.
Bài tập nhỏ
Khai báo
CoTheDaoTaovới methoddaoTao(NhanVien nv)— choQuanLyvà một lớp ngoài câyNhanViencùng implement, rồi viếttoChucKhoaHoc(CoTheDaoTao)phục vụ cả hai.Thêm default method
moTa()vào hai interface khác nhau với cùng chữ ký — implement cả hai trong một lớp và xử lý lỗi biên dịch bằngTenInterface.super.moTa().Đánh dấu
BoLocNhanVienbằng@FunctionalInterface— thử thêm method thứ hai để quan sát thông báo lỗi, rồi viết lại anonymous class thành lambda.Đọc mã nguồn
Comparatortrong JDK: đâu là abstract method duy nhất, đâu là default/static method? Đối chiếu với nội dung hôm nay.
Kết luận
Interface hoàn thiện hệ thống kiểu của Java: hợp đồng thuần túy tách khỏi cây kế thừa, một lớp cam kết nhiều năng lực cùng lúc, default method cho phép API tiến hóa không phá vỡ mã cũ, và functional interface mở cửa sang lập trình hàm. Cùng với abstract class (Ngày 16), bạn đã có đủ hai công cụ trừu tượng hóa quan trọng nhất của OOP. Ngày 18 ta xem xét hai từ khóa nhỏ nhưng ảnh hưởng lớn đến thiết kế: static và final — khi nào thành viên thuộc về lớp thay vì đối tượng, và khi nào nên khóa lại không cho thay đổi. Hẹn gặp lại!
Site Admin
Engineer and writer. Building things with TypeScript and distributed systems.
Bình luận (0)
Bạn cần đăng nhập bằng Google để bình luận.
Hãy là người bình luận đầu tiên.


