Interface: bản chất, hợp đồng trong Java và Kotlin cho Android

Tác giả: IT Sectr Đã đăng: 2026-02-18 Thời gian đọc: 9 phút

Interface là một hợp đồng xác định tập hợp các phương thức trừu tượng mà một lớp phải triển khai. Trong Java và Kotlin, interface là cơ chế chính của sự trừu tượng và đa hình. Trong Java 8+, interface có thể chứa phương thức default và static, trong Kotlin — triển khai mặc định. Theo Google Android Developers (2025), interface được sử dụng trong 90% dự án Android để định nghĩa các tầng kiến trúc — repository, UseCase và dịch vụ.

Những điểm chính

  • Interface là kiểu trừu tượng định nghĩa chữ ký phương thức mà không có triển khai (trước Java 8)
  • Implements là từ khóa liên kết lớp với interface; một lớp có thể triển khai nhiều interface
  • Default method là phương thức có triển khai trong interface Java, được thêm vào Java 8 để tương thích ngược
  • Kotlin interface hỗ trợ thuộc tính với getter và triển khai phương thức, thay thế abstract class trong nhiều kịch bản
  • Markup interface là interface trống được dùng làm đánh dấu (Serializable, Cloneable, RandomAccess)

Interface trong Java và Kotlin là gì?

Interface là một kiểu tham chiếu chứa các phương thức trừu tượng, hằng số và phương thức mặc định. Một lớp triển khai interface phải cung cấp triển khai cho tất cả các phương thức trừu tượng của nó. Trong Java, interface không thể có trạng thái (trường instance); Kotlin cũng tuân theo hạn chế này nhưng hỗ trợ thuộc tính với bộ truy cập.

Java
public interface Repository {
    T findById(Long id);
    List findAll();
    T save(T entity);
    void deleteById(Long id);
    
    default long count() {
        return findAll().size();
    }
}

@Entity
public class UserEntity {
    private Long id;
    private String email;
    // getter và setter
}

public class UserRepositoryImpl implements Repository {
    private final EntityManager em;
    
    public UserEntity findById(Long id) {
        return em.find(UserEntity.class, id);
    }
    
    public List findAll() {
        return em.createQuery("FROM UserEntity", UserEntity.class)
                .getResultList();
    }
    // các phương thức khác
}

Repository<T> là một interface generic với các phương thức CRUD. Phương thức default count() cung cấp triển khai mặc định có thể được ghi đè. UserRepositoryImpl triển khai interface sử dụng EntityManager để truy cập dữ liệu. Cách tiếp cận này cho phép kiểm thử tầng dữ liệu thông qua mock của interface mà không cần cơ sở dữ liệu thực.

Interface vs abstract class: khi nào chọn cái gì

Lựa chọn giữa interface và lớp trừu tượng phụ thuộc vào sự hiện diện của trạng thái chia sẻ và mối quan hệ giữa các kiểu. Interface định nghĩa một hợp đồng (lớp có thể làm gì), lớp trừu tượng định nghĩa một triển khai chung (lớp là gì).

Tiêu chíInterfaceAbstract Class
Trạng thái (trường)Chỉ hằng số static finalCó, bất kỳ trường nào
Hàm tạoKhông
Đa kế thừaCó (implements)Không (extends một)
Bổ từ truy cậppublic (Java 8-), phương thức defaultTất cả (private, protected, public)
Khi nào sử dụngHợp đồng cho các lớp không liên quanCơ sở chung cho các lớp liên quan

Trong Clean Architecture, interface được đặt ở tầng bên trong (domain), và triển khai ở tầng bên ngoài (data). Điều này cho phép duy trì Dependency Rule: tầng ngoài phụ thuộc vào tầng trong, nhưng không ngược lại. Lớp trừu tượng thường được sử dụng cho các phương thức khuôn mẫu (Template Method pattern).

Interface trong Kotlin: thuộc tính và ủy quyền

Interface Kotlin linh hoạt hơn Java: chúng có thể khai báo thuộc tính trừu tượng và cung cấp triển khai phương thức. Không giống Java, Kotlin hỗ trợ ủy quyền qua từ khóa by, giúp giảm đáng kể boilerplate khi triển khai mẫu Delegate.

Kotlin
interface ApiService {
    val baseUrl: String  // thuộc tính trừu tượng
    
    suspend fun fetchData(): Result>
    
    fun getEndpoint(path: String): String {
        return "$baseUrl/$path"  // triển khai mặc định
    }
}

class RetrofitApiService(
    override val baseUrl: String
) : ApiService {
    private val client = Retrofit.Builder()
        .baseUrl(baseUrl)
        .build()
    
    override suspend fun fetchData(): Result> {
        // triển khai yêu cầu
    }
}

// Ủy quyền qua by
interface Logger {
    fun log(message: String)
}

class ConsoleLogger : Logger {
    override fun log(message: String) = println(message)
}

class UserService(logger: Logger) : Logger by logger

UserService triển khai interface Logger thông qua ủy quyền (by). Tất cả các cuộc gọi log() được chuyển tiếp đến đối tượng logger mà không cần viết phương thức bao. Đây là một ví dụ về hợp thành mà trong Java sẽ cần 5-10 dòng mã boilerplate.

Interface trong Clean Architecture Android

Clean Architecture cho Android phân chia ứng dụng rõ ràng thành các tầng. Interface đóng vai trò là ranh giới giữa các tầng: Domain định nghĩa interface repository và UseCase, Data cung cấp triển khai. Điều này cho phép thay thế triển khai mà không thay đổi logic nghiệp vụ — một lợi thế chính khi di chuyển từ Room sang Firebase hoặc từ REST sang GraphQL.

Kotlin
// Domain layer — interface (hợp đồng)
interface UserRepository {
    suspend fun getUser(id: String): User
    suspend fun updateUser(user: User)
}

// Domain layer — use case (phụ thuộc vào interface)
class GetUserUseCase(
    private val repository: UserRepository
) {
    suspend operator fun invoke(id: String): Result {
        return runCatching { repository.getUser(id) }
    }
}

// Data layer — triển khai
class UserRepositoryImpl(
    private val api: UserApi,
    private val dao: UserDao
) : UserRepository {
    override suspend fun getUser(id: String): User {
        val cached = dao.getUser(id)
        if (cached != null) return cached
        val remote = api.fetchUser(id)
        dao.insertUser(remote)
        return remote
    }
    // updateUser...
}

UserRepository là một interface trong tầng domain. GetUserUseCase phụ thuộc vào interface, không phải triển khai. UserRepositoryImpl trong tầng data triển khai interface, kết hợp API và cơ sở dữ liệu cục bộ. Kiến trúc này cho phép kiểm thử UseCases với repository mock mà không cần thiết lập cơ sở dữ liệu hoặc mạng.

Phương thức default và static trong Java 8+

Phương thức default được thêm vào Java 8 để mở rộng tiến hóa của interface mà không phá vỡ tương thích ngược. Nếu ArrayList không triển khai phương thức stream() mới được thêm vào Collection, mã cũ vẫn tiếp tục hoạt động. Phương thức static trong interface phục vụ như các tiện ích liên quan đến interface — một giải pháp thay thế cho các lớp tiện ích.

Java
public interface Vehicle {
    void start();
    void stop();
    
    default void honk() {
        System.out.println("Beep!");
    }
    
    static Vehicle of(String type) {
        if ("car".equals(type)) return new Car();
        return new Bicycle();
    }
    
    // hằng số
    String CATEGORY = "transport";
}

Phương thức default giải quyết vấn đề kim cương: nếu một lớp triển khai hai interface có cùng phương thức default, trình biên dịch yêu cầu ghi đè rõ ràng. Phương thức static được gọi thông qua tên interface — Vehicle.of("car"), mà không cần instance.

Lỗi thường gặp khi thiết kế interface

Lỗi trong thiết kế interface dẫn đến mã dễ vỡ, phức tạp kiểm thử và vi phạm SOLID. Hãy xem xét ba vấn đề phổ biến.

Interface Pollution — quá nhiều phương thức trong một interface

Interface chứa 15+ phương thức vi phạm Interface Segregation Principle (ISP). Một ví dụ là java.util.Dictionary cũ với 10+ phương thức. Giải pháp: chia thành nhiều interface nhỏ — ReadableRepository, WritableRepository, SearchableRepository. Client (dịch vụ) chỉ phụ thuộc vào các phương thức nó cần.

Trừu tượng quá mức — interface cho mọi lớp

Tạo interface cho mọi lớp mà không có nhu cầu thực sự về đa hình là phản mẫu Interface overkill. Dấu hiệu: interface có đúng một triển khai và dự án không có kế hoạch thêm giải pháp thay thế. Giải pháp: chỉ thêm interface khi xuất hiện tùy chọn triển khai thứ hai hoặc cần mock cho kiểm thử.

Câu hỏi thường gặp

Interface khác abstract class trong Java như thế nào?

Interface chỉ định nghĩa hợp đồng (chữ ký phương thức), không thể có trạng thái và hỗ trợ đa kế thừa. Abstract class có thể chứa trường, hàm tạo và phương thức đã triển khai, nhưng một lớp chỉ có thể kế thừa một lớp trừu tượng. Từ Java 8, interface đã có phương thức default và static, thu hẹp khoảng cách.

Interface có thể kế thừa interface khác không?

Có, trong Java và Kotlin, interface hỗ trợ kế thừa. public interface AdvancedRepository<T> extends Repository<T>, Pageable là một interface kết hợp hai interface khác. Lớp triển khai AdvancedRepository phải triển khai tất cả phương thức của cả hai interface cha. Đa kế thừa chỉ được phép cho interface.

Interface hàm trong Java là gì?

Interface hàm là interface có một phương thức trừu tượng duy nhất (SAM — Single Abstract Method). Chú thích @FunctionalInterface đảm bảo hạn chế này. Ví dụ: Runnable, Callable, Comparator, Consumer. Interface hàm là nền tảng của biểu thức lambda Java 8: () -> System.out.println() triển khai Runnable.

Tại sao cần phương thức default trong interface Java?

Phương thức default cho phép thêm phương thức mới vào interface mà không sửa đổi tất cả các lớp triển khai. Ví dụ, Java 8 đã thêm stream() vào Collection như một phương thức default. Nếu không có cơ chế này, foreach(), stream() và các phương thức khác sẽ yêu cầu thay đổi hàng nghìn lớp trong JDK. Default là cách mở rộng tương thích ngược.

Kotlin giải quyết vấn đề đa kế thừa như thế nào?

Kotlin cấm đa kế thừa lớp nhưng cho phép triển khai nhiều interface. Nếu hai interface có phương thức cùng chữ ký và triển khai mặc định, trình biên dịch yêu cầu ghi đè rõ ràng với lời gọi super<InterfaceName>.method(). Điều này giải quyết vấn đề kim cương ở cấp độ biên dịch.

Tổng kết

  • Interface là hợp đồng định nghĩa phương thức mà lớp phải triển khai; cơ chế đa hình chính trong Java và Kotlin
  • Java 8+ đã thêm phương thức default và static vào interface, thu hẹp khoảng cách với lớp trừu tượng
  • Kotlin interfaces hỗ trợ thuộc tính trừu tượng, triển khai mặc định và ủy quyền qua by
  • Clean Architecture sử dụng interface làm ranh giới giữa tầng Domain và Data
  • Interface Segregation Principle yêu cầu chia interface lớn thành các interface chuyên biệt
  • Interface hàm (Single Abstract Method) là nền tảng của biểu thức lambda và Stream API
  • Khuyến nghị: thêm interface khi xuất hiện triển khai thứ hai hoặc cần mock để kiểm thử

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm