
Chào các chiến thần GenZ, hôm nay chúng ta sẽ cùng anh Creyt "bóc tách" một khái niệm siêu "cool" trong Java, đó chính là từ khóa implements. Nghe có vẻ khô khan nhưng tin anh đi, nó thú vị hơn cả việc lướt TikTok đấy!
implements là gì và để làm gì? (aka "Hợp đồng Code" của GenZ)
Trong thế giới lập trình, implements giống như một bản hợp đồng ràng buộc giữa một Class và một Interface. Tưởng tượng thế này: bạn muốn xây dựng một ứng dụng quản lý "pet cưng" với các loại pet khác nhau như chó, mèo, chim... Mỗi con pet dù khác loài nhưng đều có những hành vi chung như "ăn", "ngủ", "chơi".
Thay vì viết đi viết lại từng hành vi cho từng loài, chúng ta tạo ra một Interface – tạm gọi là HanhViThuCung. Interface này chỉ định nghĩa những hành vi cần có (như an(), ngu(), choi()) mà không quan tâm đến cách chúng được thực hiện. Nó giống như một danh sách "to-do list" mà thôi.
Khi một Class (ví dụ Class Cho hay Class Meo) sử dụng từ khóa implements HanhViThuCung, nó đang ký vào bản hợp đồng đó. Và khi đã ký thì bắt buộc phải thực hiện tất cả các điều khoản trong hợp đồng – tức là phải cung cấp phần thân (implementation) cho tất cả các phương thức đã được khai báo trong Interface (an(), ngu(), choi()). Nếu không, Java compiler sẽ "giận dỗi" và không cho code của bạn chạy đâu!
Vậy implements sinh ra để làm gì?
- Đảm bảo tính nhất quán (Consistency): Giúp các đối tượng khác nhau (Chó, Mèo) dù có cách thực hiện khác nhau nhưng vẫn "nói chuyện" được với nhau qua một giao diện chung. Như kiểu mọi app mạng xã hội đều có chức năng "đăng bài", nhưng cách đăng bài của TikTok khác với Facebook vậy.
- Tính mở rộng (Extensibility): Dễ dàng thêm các loại pet mới (Chim, Cá...) vào ứng dụng mà không làm ảnh hưởng đến cấu trúc code hiện có. Chỉ cần tạo
Class Chim implements HanhViThuCunglà xong. - Đa hình (Polymorphism): Cho phép bạn coi nhiều đối tượng thuộc các class khác nhau như cùng một kiểu nếu chúng cùng
implementsmột interface. "Mọi con vật có thể ăn", nên bạn có thể tạo một danh sáchList<HanhViThuCung>chứa cả Chó, Mèo, Chim.
Code Ví Dụ Minh Hoạ: Điện Thoại Thông Minh
Để dễ hình dung hơn, chúng ta hãy xem ví dụ về các hãng điện thoại thông minh. Mỗi hãng có cách thực hiện riêng nhưng đều có những chức năng cốt lõi như gọi điện, nhắn tin, chụp ảnh.
// Bước 1: Tạo "hợp đồng" - Interface định nghĩa các hành vi cốt lõi của điện thoại
interface HanhViDienThoai {
void goiDien(String soDienThoai);
void nhanTin(String soDienThoai, String tinNhan);
void chupAnh();
}
// Bước 2: "Nhà sản xuất" Apple ký hợp đồng và thực hiện lời hứa của mình
class IPhone implements HanhViDienThoai {
@Override // Annotation này giúp kiểm tra xem phương thức có override đúng không
public void goiDien(String soDienThoai) {
System.out.println("IPhone đang gọi đến số: " + soDienThoai + " bằng FaceTime Audio.");
}
@Override
public void nhanTin(String soDienThoai, String tinNhan) {
System.out.println("IPhone đang gửi iMessage đến số: " + soDienThoai + " với nội dung: '" + tinNhan + "'");
}
@Override
public void chupAnh() {
System.out.println("IPhone chụp ảnh với chế độ Portrait Mode siêu nét!");
}
// IPhone có thể có các hành vi riêng khác không có trong hợp đồng
public void dungSiri() {
System.out.println("Hey Siri, mở nhạc!");
}
}
// Bước 3: "Nhà sản xuất" Samsung cũng ký hợp đồng và thực hiện lời hứa theo cách riêng
class Samsung implements HanhViDienThoai {
@Override
public void goiDien(String soDienThoai) {
System.out.println("Samsung đang gọi đến số: " + soDienThoai + " qua mạng 5G.");
}
@Override
public void nhanTin(String soDienThoai, String tinNhan) {
System.out.println("Samsung đang gửi SMS đến số: " + soDienThoai + " với nội dung: '" + tinNhan + "'");
}
@Override
public void chupAnh() {
System.out.println("Samsung chụp ảnh với chế độ Night Mode cực đỉnh!");
}
// Samsung cũng có thể có các hành vi riêng khác
public void dungBixby() {
System.out.println("Hi Bixby, bật đèn!");
}
}
// Bước 4: Chạy thử và thấy sức mạnh của "hợp đồng" chung
public class DienThoaiApp {
public static void main(String[] args) {
// Khai báo kiểu Interface, nhưng khởi tạo bằng class cụ thể (đa hình)
HanhViDienThoai myIphone = new IPhone();
HanhViDienThoai mySamsung = new Samsung();
System.out.println("--- Dùng IPhone --- ");
myIphone.goiDien("0912345678");
myIphone.nhanTin("0987654321", "Alo, bạn khỏe không?");
myIphone.chupAnh();
// myIphone.dungSiri(); // Lỗi! Không thể gọi phương thức riêng qua kiểu interface HanhViDienThoai
System.out.println("\n--- Dùng Samsung --- ");
mySamsung.goiDien("0334567890");
mySamsung.nhanTin("0909090909", "Hẹn gặp nhé!");
mySamsung.chupAnh();
// Một hàm có thể nhận bất kỳ đối tượng nào implement HanhViDienThoai
System.out.println("\n--- Kiểm tra chung các điện thoại --- ");
kiemTraDienThoai(myIphone);
kiemTraDienThoai(mySamsung);
}
// Hàm này không cần biết đó là IPhone hay Samsung, chỉ cần biết nó là "một cái điện thoại"
public static void kiemTraDienThoai(HanhViDienThoai dt) {
System.out.println("--- Đang kiểm tra một điện thoại bất kỳ ---");
dt.goiDien("113");
dt.chupAnh();
}
}

Mẹo hay (Best Practices) để "chơi" với implements
- Nhớ "ký hợp đồng" đầy đủ: Khi bạn
implementsmộtInterface, hãy chắc chắn rằng bạn đã cung cấp phần thân cho tất cả các phương thức được khai báo trong đó. Đây là quy tắc vàng, nếu không compiler sẽ "gank" bạn ngay lập tức. Interfacelà "cánh cổng": Thay vì khai báo biến hay tham số hàm bằng kiểuClasscụ thể (ví dụIPhone myPhone = new IPhone();), hãy dùng kiểuInterface(ví dụHanhViDienThoai myPhone = new IPhone();). Điều này giúp code của bạn linh hoạt hơn, dễ dàng thay đổi loại điện thoại mà không cần sửa nhiều chỗ.- Tên
Interfacecó "hint": Thường thì cácInterfacetrong Java hay có tên kết thúc bằng-able(ví dụRunnable,Comparable,Serializable) hoặc đôi khi bắt đầu bằng chữI(nhưIListtrong C#, dù Java ít dùng hơn). Điều này giúp bạn dễ nhận diện đó là mộtInterface. - "Hợp đồng" nhỏ, chuyên biệt: Đừng cố gắng tạo ra một
Interfacequá lớn, ôm đồm quá nhiều chức năng. MỗiInterfacenên tập trung vào một nhóm hành vi cụ thể, rõ ràng. Điều này giúp code dễ hiểu, dễ quản lý và tái sử dụng hơn. default methods(Java 8+): Đây là một "điều khoản phụ" cực hay trong hợp đồng. Nó cho phép bạn thêm một phương thức có cài đặt mặc định vàoInterfacemà không làm hỏng cácClassđãimplementsnó trước đó. Như kiểu thêm một tính năng mới vào điện thoại mà các hãng không cần phải cập nhật lại từ đầu vậy.
Ví dụ thực tế các ứng dụng/website đã ứng dụng
implements và Interface là xương sống của rất nhiều framework và thư viện Java:
- Android Development: Khi bạn tạo các nút bấm, ô nhập liệu trên ứng dụng Android, bạn thường phải
implementscácListenernhưOnClickListenerhayTextWatcher. Đây là cách bạn nói cho hệ thống biết "khi có sự kiện này xảy ra, hãy gọi phương thức của tôi". - Java Collections Framework: Các cấu trúc dữ liệu quen thuộc như
List,Set,Mapđều làInterface. CácClasscụ thể nhưArrayList,HashSet,HashMapsẽimplementschúng. Điều này cho phép bạn viết code chung choListmà không cần quan tâm nó làArrayListhayLinkedListphía dưới. - Spring Framework: Trong Spring, bạn sẽ thấy rất nhiều
Interfaceđược dùng để định nghĩa các dịch vụ (Services), kho lưu trữ dữ liệu (Repositories). Điều này giúp bạn dễ dàng thay đổi cách thức lưu trữ dữ liệu (ví dụ từ MySQL sang MongoDB) mà không cần chỉnh sửa quá nhiều code ở tầng logic ứng dụng. - JDBC (Java Database Connectivity): Các đối tượng như
Connection,Statement,ResultSetđều làInterface. Các nhà cung cấp cơ sở dữ liệu (Oracle, MySQL, PostgreSQL) sẽ cung cấp các driver chứa cácClasscụ thểimplementsnhữngInterfacenày, giúp bạn kết nối và thao tác với nhiều loại database khác nhau chỉ với một bộ API chung.
Thử nghiệm đã từng và hướng dẫn nên dùng cho case nào
Với kinh nghiệm "chinh chiến" của anh Creyt, anh đã từng thấy nhiều bạn trẻ (và cả anh ngày xưa nữa) loay hoay giữa extends và implements. Nhớ kỹ điều này:
extends(kế thừa Class): Dùng khi có mối quan hệ "là một loại của" (is-a relationship) và bạn muốn tái sử dụng code đã được hiện thực hóa. Ví dụ:Chó extends ĐộngVật(Chó là một loại Động Vật).implements(thực thi Interface): Dùng khi có mối quan hệ "có khả năng làm" (has-a capability) hoặc "có hành vi" (has-a behavior) và bạn muốn định nghĩa một hợp đồng về hành vi mà không quan tâm đến cách nó được thực hiện. MộtClasscó thểimplementsnhiềuInterface(ký nhiều hợp đồng), nhưng chỉextendsmộtClassduy nhất.
Anh từng thử nghiệm việc cố gắng nhồi nhét mọi thứ vào một abstract class (class trừu tượng) để tái sử dụng code, nhưng đến lúc cần một class con có hành vi của hai "class cha" khác nhau là "tắc tị" ngay (vì Java không hỗ trợ đa kế thừa class). Đó là lúc Interface và implements trở thành "cứu tinh", cho phép một class có thể có nhiều "năng lực" khác nhau từ nhiều Interface.
Nên dùng implements cho các trường hợp sau:
- Định nghĩa API công cộng: Khi bạn thiết kế một thư viện hoặc module mà các phần khác của hệ thống (hoặc người dùng thư viện của bạn) cần tuân thủ một bộ quy tắc nhất định về cách tương tác.
- Cơ chế Callback/Event Handling: Trong lập trình sự kiện, một đối tượng cần "thông báo" cho đối tượng khác khi có điều gì đó xảy ra. Đối tượng nhận thông báo sẽ
implementsmộtInterfacecallback để định nghĩa cách nó sẽ phản ứng. - Strategy Pattern: Đây là một Design Pattern (mẫu thiết kế) nổi tiếng. Bạn định nghĩa một "họ" các thuật toán, đóng gói mỗi thuật toán thành một
Classriêng biệt, và làm cho chúng có thể hoán đổi cho nhau. Mỗi thuật toán sẽimplementsmộtInterfacechung. - Dependency Inversion Principle (DIP) trong SOLID: Một trong 5 nguyên tắc SOLID, khuyến khích bạn phụ thuộc vào các
abstraction(Interface) thay vì cácconcrete implementation(Class cụ thể). Điều này giúp code dễ kiểm thử (testable), dễ bảo trì và mở rộng hơn rất nhiều.
Đó là tất tần tật về implements keyword, một trong những "siêu năng lực" của OOP trong Java. Hãy thực hành thật nhiều để biến nó thành kỹ năng của riêng bạn nhé, các GenZ! Code là phải "chất", phải "ngầu"!
Thuộc Series: Java – OOP
Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!