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
) : 

인터페이스 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() 함수 모두 해당된다.

참고