Interface: แก่นแท้, สัญญาใน Java และ Kotlin สำหรับ Android

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-02-18 เวลาอ่าน: 9 นาที

Interface คือสัญญาที่กำหนดชุดของเมธอดนามธรรมที่คลาสต้อง implement ใน Java และ Kotlin อินเทอร์เฟซเป็นกลไกหลักของนามธรรมและพหุนาม ใน Java 8+ อินเทอร์เฟซสามารถมีเมธอด default และ static ได้ ใน Kotlin — การ implement เริ่มต้น ตามข้อมูลจาก Google Android Developers (2025) อินเทอร์เฟซถูกใช้ใน 90% ของโปรเจกต์ Android เพื่อกำหนดเลเยอร์สถาปัตยกรรม — repository, UseCase และบริการ

ประเด็นสำคัญ

  • Interface คือชนิดนามธรรมที่กำหนดลายเซ็นเมธอดโดยไม่มีการ implement (ก่อน Java 8)
  • Implements คือคำสำคัญที่เชื่อมคลาสกับอินเทอร์เฟซ; คลาสสามารถ implement อินเทอร์เฟซหลายตัวได้
  • Default method คือเมธอดที่มี implement ในอินเทอร์เฟซ Java เพิ่มใน Java 8 เพื่อความเข้ากันได้ย้อนหลัง
  • Kotlin interface รองรับคุณสมบัติกับ getter และการ implement เมธอด แทนที่ abstract class ในหลายสถานการณ์
  • Markup interface คืออินเทอร์เฟซว่างที่ใช้เป็นตัวบอก (Serializable, Cloneable, RandomAccess)

Interface ใน Java และ Kotlin คืออะไร?

Interface คือชนิดอ้างอิงที่ประกอบด้วยเมธอดนามธรรม ค่าคงที่ และเมธอดเริ่มต้น คลาสที่ implement อินเทอร์เฟซต้องให้ implement สำหรับเมธอดนามธรรมทั้งหมด ใน Java อินเทอร์เฟซไม่สามารถมีสถานะ (ฟิลด์ instance) ได้ Kotlin ก็ปฏิบัติตามข้อจำกัดนี้เช่นกัน แต่รองรับคุณสมบัติพร้อมตัวเข้าถึง

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 และ 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();
    }
    // เมธอดอื่น ๆ
}

Repository<T> คืออินเทอร์เฟซทั่วไปที่มีเมธอด CRUD เมธอด default count() ให้ implement เริ่มต้นที่สามารถแทนที่ได้ UserRepositoryImpl implement อินเทอร์เฟซโดยใช้ EntityManager สำหรับการเข้าถึงข้อมูล วิธีนี้ช่วยให้ทดสอบเลเยอร์ข้อมูลผ่าน mock ของอินเทอร์เฟซโดยไม่ต้องมีฐานข้อมูลจริง

Interface vs abstract class: เมื่อไรเลือกอะไร

การเลือกระหว่างอินเทอร์เฟซและคลาสนามธรรม ขึ้นอยู่กับการมีสถานะร่วมและความสัมพันธ์ระหว่างชนิด อินเทอร์เฟซกำหนดสัญญา (สิ่งที่คลาสทำได้) คลาสนามธรรมกำหนด implement ร่วม (สิ่งที่คลาสเป็น)

เกณฑ์InterfaceAbstract Class
สถานะ (ฟิลด์)เฉพาะค่าคงที่ static finalใช่ ฟิลด์ใดก็ได้
คอนสตรัคเตอร์ไม่มีมี
การสืบทอดหลายทางได้ (implements)ไม่ได้ (extends หนึ่ง)
ตัวปรับการเข้าถึงpublic (Java 8-), เมธอด defaultทั้งหมด (private, protected, public)
เมื่อไรใช้สัญญาสำหรับคลาสที่ไม่เกี่ยวข้องฐานร่วมสำหรับคลาสที่เกี่ยวข้อง

ใน Clean Architecture อินเทอร์เฟซถูกวางในเลเยอร์ด้านใน (domain) และ implement ในเลเยอร์ด้านนอก (data) ซึ่งช่วยให้คงกฎการพึ่งพา (Dependency Rule): เลเยอร์ด้านนอกขึ้นอยู่กับเลเยอร์ด้านใน แต่ไม่ใช่ในทางกลับกัน คลาสนามธรรมมักใช้สำหรับเมธอดแม่แบบ (Template Method pattern)

Interface ใน Kotlin: คุณสมบัติและตัวแทน

อินเทอร์เฟซ Kotlin ยืดหยุ่นกว่า Java: สามารถประกาศคุณสมบัตินามธรรมและให้ implement เมธอดได้ แตกต่างจาก Java Kotlin รองรับการมอบหมายผ่านคำสำคัญ by ซึ่งช่วยลดโค้ดสำเร็จรูปอย่างมากเมื่อ implement รูปแบบ Delegate

Kotlin
interface ApiService {
    val baseUrl: String  // คุณสมบัตินามธรรม
    
    suspend fun fetchData(): Result>
    
    fun getEndpoint(path: String): String {
        return "$baseUrl/$path"  // implement เริ่มต้น
    }
}

class RetrofitApiService(
    override val baseUrl: String
) : ApiService {
    private val client = Retrofit.Builder()
        .baseUrl(baseUrl)
        .build()
    
    override suspend fun fetchData(): Result> {
        // การ implement คำขอ
    }
}

// การมอบหมายผ่าน 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 implement อินเทอร์เฟซ Logger ผ่านการมอบหมาย (by) การเรียก log() ทั้งหมดถูกส่งต่อไปยังออบเจกต์ logger โดยไม่ต้องเขียนเมธอดหุ้ม นี้คือตัวอย่างของการประกอบที่ใน Java ต้องใช้โค้ดสำเร็จรูป 5-10 บรรทัด

Interface ใน Clean Architecture Android

Clean Architecture สำหรับ Android แบ่งแอปพลิเคชันออกเป็นเลเยอร์อย่างชัดเจน อินเทอร์เฟซทำหน้าที่เป็นขอบเขตระหว่างเลเยอร์: Domain กำหนดอินเทอร์เฟซ repository และ UseCase Data ให้ implement ซึ่งช่วยให้เปลี่ยน implement ได้โดยไม่ต้องเปลี่ยนตรรกะทางธุรกิจ — ข้อได้เปรียบสำคัญเมื่อย้ายจาก Room ไป Firebase หรือจาก REST ไป GraphQL

Kotlin
// Domain layer — อินเทอร์เฟซ (สัญญา)
interface UserRepository {
    suspend fun getUser(id: String): User
    suspend fun updateUser(user: User)
}

// Domain layer — use case (ขึ้นอยู่กับอินเทอร์เฟซ)
class GetUserUseCase(
    private val repository: UserRepository
) {
    suspend operator fun invoke(id: String): Result {
        return runCatching { repository.getUser(id) }
    }
}

// Data layer — implement
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 คืออินเทอร์เฟซในเลเยอร์ domain GetUserUseCase ขึ้นอยู่กับอินเทอร์เฟซ ไม่ใช่ implement UserRepositoryImpl ในเลเยอร์ data implement อินเทอร์เฟซ โดยรวม API และฐานข้อมูลท้องถิ่น สถาปัตยกรรมนี้ช่วยให้ทดสอบ UseCases กับ repository mock โดยไม่ต้องตั้งค่าฐานข้อมูลหรือเครือข่าย

เมธอด default และ static ใน Java 8+

เมธอด default ถูกเพิ่มใน Java 8 สำหรับการขยายอินเทอร์เฟซแบบวิวัฒนาการโดยไม่ทำลายความเข้ากันได้ย้อนหลัง ถ้า ArrayList ไม่ได้ implement เมธอด stream() ใหม่ที่เพิ่มใน Collection โค้ดเก่าจะยังคงทำงานต่อไป เมธอด static ในอินเทอร์เฟซทำหน้าที่เป็นยูทิลิตี้ที่เกี่ยวข้องกับอินเทอร์เฟซ — ทางเลือกแทนคลาสยูทิลิตี้

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();
    }
    
    // ค่าคงที่
    String CATEGORY = "transport";
}

เมธอด default แก้ ปัญหาเพชร (diamond problem): ถ้าคลาส implement อินเทอร์เฟซสองตัวที่มีเมธอด default เดียวกัน คอมไพเลอร์ต้องการการแทนที่อย่างชัดแจ้ง เมธอด static ถูกเรียกผ่านชื่ออินเทอร์เฟซ — Vehicle.of("car") โดยไม่มี instance

ข้อผิดพลาดทั่วไปในการออกแบบอินเทอร์เฟซ

ข้อผิดพลาดในการออกแบบอินเทอร์เฟซ นำไปสู่โค้ดที่เปราะบาง ความซับซ้อนในการทดสอบ และการละเมิด SOLID มาดูปัญหาทั่วไปสามข้อ

Interface Pollution — เมธอดมากเกินไปในอินเทอร์เฟซเดียว

อินเทอร์เฟซที่มี 15+ เมธอดละเมิด Interface Segregation Principle (ISP) ตัวอย่างคือ java.util.Dictionary เก่าที่มี 10+ เมธอด วิธีแก้: แบ่งเป็นอินเทอร์เฟซเล็กหลายอัน — ReadableRepository, WritableRepository, SearchableRepository ไคลเอนต์ (บริการ) ขึ้นอยู่กับเมธอดที่ต้องการเท่านั้น

นามธรรมมากเกินไป — อินเทอร์เฟซสำหรับทุกคลาส

การสร้างอินเทอร์เฟซสำหรับทุกคลาสโดยไม่มีความต้องการพหุนามที่แท้จริงคือปฏิยานุกรม Interface overkill ตัวบ่งชี้: อินเทอร์เฟซมี implement เพียงหนึ่งเดียวและโปรเจกต์ไม่มีแผนเพิ่มทางเลือก วิธีแก้: เพิ่มอินเทอร์เฟซเมื่อมีตัวเลือก implement ที่สองปรากฏขึ้นหรือต้องการ mock สำหรับการทดสอบ

คำถามที่พบบ่อย

Interface ต่างจาก abstract class ใน Java อย่างไร?

Interface กำหนดเพียงสัญญา (ลายเซ็นเมธอด) ไม่สามารถมีสถานะและรองรับการสืบทอดหลายทาง Abstract class สามารถมีฟิลด์ คอนสตรัคเตอร์ และเมธอดที่ implement แล้ว แต่คลาสสามารถสืบทอดคลาสนามธรรมได้เพียงหนึ่งเดียว ตั้งแต่ Java 8 เป็นต้นมา อินเทอร์เฟซได้รับเมธอด default และ static ทำให้ช่องว่างแคบลง

อินเทอร์เฟซสามารถสืบทอดอินเทอร์เฟซอื่นได้หรือไม่?

ใช่ ใน Java และ Kotlin อินเทอร์เฟซรองรับการสืบทอด public interface AdvancedRepository<T> extends Repository<T>, Pageable คืออินเทอร์เฟซที่รวมอีกสองอัน คลาสที่ implement AdvancedRepository ต้อง implement เมธอดทั้งหมดของอินเทอร์เฟซหลักทั้งสอง การสืบทอดหลายทางอนุญาตเฉพาะสำหรับอินเทอร์เฟซ

อินเทอร์เฟซฟังก์ชันใน Java คืออะไร?

อินเทอร์เฟซฟังก์ชัน คืออินเทอร์เฟซที่มีเมธอดนามธรรมเดี่ยว (SAM — Single Abstract Method) คำอธิบายประกอบ @FunctionalInterface รับประกันข้อจำกัดนี้ ตัวอย่าง: Runnable, Callable, Comparator, Consumer อินเทอร์เฟซฟังก์ชันเป็นพื้นฐานของนิพจน์ lambda ใน Java 8: () -> System.out.println() implement Runnable

ทำไมต้องมีเมธอด default ในอินเทอร์เฟซ Java?

เมธอด default ช่วยให้เพิ่มเมธอดใหม่ในอินเทอร์เฟซโดยไม่ต้องแก้ไขคลาสที่ implement ทั้งหมด ตัวอย่างเช่น Java 8 เพิ่ม stream() ใน Collection เป็นเมธอด default หากไม่มีกลไกนี้ foreach(), stream() และเมธอดอื่น ๆ จะต้องเปลี่ยนแปลงคลาสนับพันใน JDK Default เป็นวิธีขยายที่เข้ากันได้ย้อนหลัง

Kotlin แก้ปัญหาการสืบทอดหลายทางอย่างไร?

Kotlin ห้ามการสืบทอดหลายทางของคลาสแต่อนุญาตให้ implement อินเทอร์เฟซหลายตัว ถ้าอินเทอร์เฟซสองตัวมีเมธอดที่มีลายเซ็นและ implement เริ่มต้นเดียวกัน คอมไพเลอร์ต้องการการแทนที่อย่างชัดแจ้งด้วยการเรียก super<InterfaceName>.method() ซึ่งแก้ปัญหาเพชรในระดับการคอมไพล์

สรุป

  • Interface คือสัญญาที่กำหนดเมธอดที่คลาสต้อง implement; กลไกหลักของพหุนามใน Java และ Kotlin
  • Java 8+ เพิ่มเมธอด default และ static ในอินเทอร์เฟซ ลดช่องว่างกับคลาสนามธรรม
  • Kotlin interfaces รองรับคุณสมบัตินามธรรม implement เริ่มต้นและการมอบหมายผ่าน by
  • Clean Architecture ใช้อินเทอร์เฟซเป็นขอบเขตระหว่างเลเยอร์ Domain และ Data
  • Interface Segregation Principle ต้องการแบ่งอินเทอร์เฟซใหญ่เป็นอินเทอร์เฟซเฉพาะทาง
  • อินเทอร์เฟซฟังก์ชัน (Single Abstract Method) เป็นพื้นฐานของนิพจน์ lambda และ Stream API
  • คำแนะนำ: เพิ่มอินเทอร์เฟซเมื่อ implement ที่สองปรากฏขึ้นหรือต้องการ mock สำหรับการทดสอบ

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม