Interface trong Java là một trong những khái niệm cốt lõi. Interface là phần trọng yếu trong ngôn ngữ lập trình Java và được sử dụng rất phổ biến, không chỉ trong JDK mà còn trong các design patterns của Java. Phần lớn các framework trong Java đều sử dụng interface một cách rộng rãi.

Interface trong Java cung cấp một cách để đạt được tính trừu tượng. Interface cũng được sử dụng để xác định một “hợp đồng” mà các subclasses phải thực hiện.
Ví dụ, giả sử chúng ta muốn tạo một bản vẽ gồm nhiều hình dạng khác nhau. Khi đó, ta có thể tạo một interface tên là Shape và định nghĩa tất cả các phương thức mà các đối tượng hình học khác nhau sẽ triển khai.
Để đơn giản, chúng ta chỉ cần hai phương thức:
draw()– dùng để vẽ hình,getArea()– trả về diện tích của hình đó.
Ví dụ về Interface trong Java
Dựa trên các yêu cầu đã nêu ở trên Shape interface của chúng ta sẽ trông như sau. **Shape.java**
package com.journaldev.design;
public interface Shape {
//implicitly public, static and final
public String LABLE="Shape";
//interface methods are implicitly abstract and public
void draw();
double getArea();
}
Những điểm quan trọng về Interface trong Java
interfacelà từ khóa được dùng để tạo một interface trong Java.- Chúng ta không thể khởi tạo ****một đối tượng từ interface trong Java.
- Interface cung cấp tính trừu tượng hoàn toàn. Khác với abstract class có thể chứa cả phương thức trừu tượng và phương thức có phần thân, interface không được chứa phần thân của phương thức, trừ một vài loại đặc biệt như
defaulthoặcstaticmethods từ Java 8 trở đi. - Interface không thể có constructor, vì chúng không được khởi tạo trực tiếp.
- Theo mặc định, mọi thuộc tính trong interface là public, static, final. Vì vậy, không cần chỉ rõ các modifier này, nhưng nếu có khai báo thêm thì trình biên dịch cũng không báo lỗi.
- Các phương thức trong interface mặc định là public và abstract, điều này hoàn toàn hợp lý vì chúng không có phần thân và cần được subclasses ****triển khai.
- Interface không thể kế thừa từ một class, nhưng có ****thể kế thừa từ một hoặc nhiều interface khác. Ví dụ:
public interface Shape extends Cloneablecho thấy một interface ****kế thừa từ một interface ****khác. Java hỗ trợ đa kế thừa đối với interface, nghĩa là một interface có thể kế thừa nhiều interface ****cùng lúc. - Từ khóa
implementsđược dùng trong class để triển khai interface. - Một class khi triển khai interface ****phải hiện thực tất cả các phương thức trong interface ****đó, trừ khi nó là một abstract class. Ví dụ: chúng ta có thể dùng abstract class ****
ShapeAbsđể triển khai interfaceShape, và tùy ý hiện thực các phương thức trong đó.
package com.journaldev.design;
public abstract class ShapeAbs implements Shape {
@Override
public double getArea() {
// TODO Auto-generated method stub
return 0;
}
}
- Chúng ta luôn nên cố gắng viết chương trình dựa trên interface thay vì các implementation (cài đặt cụ thể), để đảm bảo rằng các lớp triển khai sẽ luôn cung cấp phần hiện thực cần thiết. Đồng thời, nếu sau này có một cách triển khai tốt hơn xuất hiện, ta có thể dễ dàng chuyển sang mà không cần sửa đổi nhiều.
Ví dụ về việc triển khai Interface trong Java
Bây giờ, hãy cùng xem một ví dụ về cách triển khai interface Shape trong Java. Circle.java.
package com.journaldev.design;
public class Circle implements Shape {
private double radius;
public Circle(double r){
this.radius = r;
}
@Override
public void draw() {
System.out.println("Drawing Circle");
}
@Override
public double getArea(){
return Math.PI*this.radius*this.radius;
}
public double getRadius(){
return this.radius;
}
}
Lưu ý rằng lớp Circle đã triển khai đầy đủ tất cả các phương thức được định nghĩa trong interface, đồng thời cũng có thêm một số phương thức riêng của nó, chẳng hạn như getRadius(). Việc triển khai interface có thể bao gồm nhiều kiểu constructor khác nhau.
Bây giờ, chúng ta hãy cùng xem một ví dụ khác về việc triển khai interface Shape lớp Rectangle.java.
package com.journaldev.design;
public class Rectangle implements Shape {
private double width;
private double height;
public Rectangle(double w, double h){
this.width=w;
this.height=h;
}
@Override
public void draw() {
System.out.println("Drawing Rectangle");
}
@Override
public double getArea() {
return this.height*this.width;
}
}
Dưới đây là một chương trình kiểm thử minh họa cách viết mã dựa trên interface thay vì phụ thuộc vào implementation ****cụ thể. ShapeTest.java.
package com.journaldev.design;
public class ShapeTest {
public static void main(String[] args) {
//programming for interfaces not implementation
Shape shape = new Circle(10);
shape.draw();
System.out.println("Area="+shape.getArea());
//switching from one implementation to another easily
shape=new Rectangle(10,10);
shape.draw();
System.out.println("Area="+shape.getArea());
}
}
Kết quả đầu ra của chương trình ví dụ về Java interface ở trên là:
Drawing Circle
Area=314.1592653589793
Drawing Rectangle
Area=100.0
Lợi ích của Interface trong Java
- Interface cung cấp một hợp đồng (contract) chung cho tất cả các lớp triển khai, vì vậy viết mã dựa trên interface là một thói quen tốt. Điều này đảm bảo rằng các lớp triển khai không thể bỏ đi những phương thức mà ta đang sử dụng.
- Interface là một điểm khởi đầu lý tưởng để định nghĩa kiểu dữ liệu (Type) và xây dựng cấu trúc phân cấp cấp cao (top-level hierarchy)trong chương trình.
- Vì một lớp trong Java có thể implements nhiều interface, nên trong hầu hết các trường hợp, việc dùng interface như superclass là một lựa chọn tối ưu.
Hạn chế của Interface trong Java
Mặc dù interface mang lại nhiều lợi ích, nhưng nó cũng có một số hạn chế:
- Khi thiết kế dự án, ta phải lựa chọn cẩn thận các phương thức trong interface, vì không thể thêm hoặc xóa phương thức sau này mà không gây lỗi biên dịch ở tất cả các lớp đã triển khai interface đó. Điều này đôi khi khiến ta phải tạo nhiều interface mở rộng từ interface gốc, dẫn đến mã nguồn khó bảo trì.
- Nếu lớp triển khai có các phương thức riêng của nó, ta không thể gọi trực tiếp các phương thức đó từ đối tượng kiểu interface, bởi vì interface không khai báo các phương thức đó.Ví dụ, trong đoạn mã phía trên, nếu ta viết
shape.getRadius()thì sẽ bị lỗi biên dịch, vìshapecó kiểu là interfaceShape, màgetRadius()không được định nghĩa trong interface này.Để xử lý, ta có thể sử dụng typecasting như sau:
Circle c = (Circle) shape;
c.getRadius();
Mặc dù việc ép kiểu lớp (class typecasting) giúp giải quyết một số vấn đề, nhưng nó cũng có những hạn chế riêng.
Đó là tất cả những gì mình muốn chia sẻ về interface trong Java. Vì chúng ta sử dụng interface rất thường xuyên, nên việc hiểu rõ các đặc điểm của nó là điều quan trọng. Hãy chắc chắn rằng bạn sử dụng interface trong quá trình thiết kế hệ thống, và xem nó như một bản hợp đồng giữa client và các lớp con triển khai interface đó.
Cập nhật: Từ phiên bản Java 8, định nghĩa về interface đã được mở rộng với sự xuất hiện của default methods và static methods, cho phép viết phần triển khai ngay trong interface , điều mà trước đây không thể làm được.