Kotlin 인터페이스

인터페이스 (interface)

인터페이스는 추상 클래스와 동일하게 자신을 인스턴스화 할 수 없고, 구현해야 할 속성과 메소드를 정의한다. 추상 클래스가 “상속"을 위한 것이라면, 인터페이스는 서브 클래스로써의 “기능"을 정의하는 것으로, 하나의 서브 클래스가 여러 인터페이스를 구현할 수 있다.

Kotlin의 인터페이스는 추상 메소드와 구현이 포함될 수 있다. 추상 클래스와 다른 점은 프로퍼티의 상태 정보를 저장할 수 없다는 것이다.

다시 말하자면, 인터페이스에서는 프로퍼티의 초기화(initializer)를 할 수 없다.

interface User {
    val name = "devkuma" // Error: 속성 초기화는 인터페이스에서 허용되지 않는다.
}

인터페이스는 interface 키워드를 사용하여 정의된다.

interface MyInterface {
    fun bar()
    fun foo() {
      // optional body
    }
}

인터페이스 구현 (Implementing Interfaces)

한 개의 클래스나 객체는 한 개 이상의 인터페이스를 가질 수 있다. 인터페이스를 상속 받으면 반드시 인터페이스를 구현해야 한다.

class Child : MyInterface {
    override fun bar() {
        // body
    }
}

인터페이스의 프로퍼티 (Properties in Interfaces)

인터페이스에서 속성을 선언할 수 있다. 인터페이스에 선언된 속성은 추상화(abstract) 하거나 접근자(accessor)를 구현해야 한다. 인터페이스에서는 프로퍼티를 초기화할 수 없으므로 인터페이스에 선언된 접근자는 이를 참조할 수 없다.

interface MyInterface {
    val prop: Int // abstract

    val propertyWithImplementation: String
        get() = "foo"

    fun foo() {
        print(prop)
    }
}

class Child : MyInterface {
    override val prop: Int = 29
}

인터페이스 상속 (Interfaces Inheritance)

인터페이스는 다른 인터페이스를 상속할 수 있다. 따라서 상속하는 인터페이스에서는 상위 인터페이스의 멤버를 구현을 하거나 메소드 및 프로퍼티를 새로 선언할 수 있다.

이러한 인터페이스를 구현하는 클래스는 최종적으로 구현되지 않은 멤버들만 구현하면 된다.

interface Named {
    val name: String
}

interface Person : Named {
    val firstName: String
    val lastName: String

    override val name: String get() = "$firstName $lastName"
}

data class Employee(
    // implementing 'name' is not required
    override val firstName: String,
    override val lastName: String,
    val position: Position
) : Person

인터페이스 Namedname: String이 Person에서 구현이 안되었다면 Employee에서는 구현해야 한다.

오버라이딩 충돌 해결 (Resolving overriding conflicts)

여러 인터페이스를 상속을 받아 구현을 해야 할 때, 구현해야 할 메소드가 상속되는 경우가 있다.

interface A {
    fun foo() { println("A") }
    fun bar() // abstract
}

interface B {
    fun foo() { println("B") }
    fun bar() { println("bar") }
}

class C : A {
    override fun bar() { println("bar") }
}

class D : A, B {
    override fun foo() {
        super<A>.foo()
        super<B>.foo()
    }

    override fun bar() {
        super<B>.bar()
    }
}

fun main() {
    val c = C()
    c.foo()
    c.bar()

    println()

    val d = D()
    d.foo()
    d.bar()
}

인터페이스 A와 B는 모두 foo()bar() 함수를 선언하였다. A, B 둘 다 foo()를 구현하였고, A는 bar()를 구현하지 않았지만, B에서는 구현하였다.

클래스 C에서는 인터페이스 A만 상속하였으므로 bar()만 구현하면 된다.

클래스 D에서는 인터페이스 A, B를 상속하고 있으므로 중복되는 메소드들에 대한 명확한 처리가 필요하다. 이는 한 번만 구현된 bar() 함수와 여러 번 구현된 foo() 함수 모두 해당된다.

참고