본문 바로가기

Kotlin

Kotlin - 인터페이스 (Interface)

https://kotlinlang.org/docs/interfaces.html

 

Interfaces | Kotlin

 

kotlinlang.org

Kotlin 에서 인터페이스는 추상 메소드와 구현된 메소드를 모두 가질 수 있다. 상태를 저장 할 수 없다는 점이 추상 클래스(abstract)와 다르다. 인터페이스가 속성을 가질 수는 있지만 속성이 추상화 되어 있거나, 접근자(accessor)를 구현 해야 한다.

 

선언 과 구현

인터페이스는 interface 키워드를 이용해서 정의하고 클래스에서는 하나 이상의 인터페이스를 구현 할 수 있다.

interface MyInterface {
    fun bar()

    fun foo() {
        // body
    }
}

class Child: MyInterface {
    override fun bar() {
//        구현되지 않은 bar 함수를 구현해야 한다.
    }

    // foo 함수는 구현하지 않아도 된다.
}

 

인터페이스 속성 (Properties in interfaces)

인터페이스에는 속성들을 선언 할 수 있다. 인터페이스에 선언된 속성들은 추상화 되어 있거나, 접근자(accessor)가 구현 되어 있어야 한다. 인터페이스에 선언된 속성들은 backing field(속성의 실제 값을 저장하는 곳 https://jhproject.tistory.com/191 참고) 를 가질 수 없다. 그렇기 때문에 접근자는 backing field를 참조 할 수 없다.

interface MyInterface2 {
    val prop: Int // 실제 값을 할당하지 않음 (추상화, abstract)

    var prop2: String
        get() = "foo"
        set(value) {
//            field = value //backing field 를 사용할 수 없다. compile error
        }
}

class Child2: MyInterface2 {
    override val prop: Int = 29
}

 

인터페이스 상속 (Interface 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(
    // Person 인터페이스에서 이미 name을 구현 했기때문에 다시 구현 할 필요가 없다.
    override val firstName: String,
    override val lastName: String,
    val position: Position
) : Person


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

하나이상의 인터페이스를 구현하는 경우 같은 이름의 함수가 여러 인터페이스에 선언 되어 있는경우 충돌이 발생하며, 구현 클래스에서 정확히 해당 함수가 어떻게 동작하는지를 정의 해 줘야 한다.

interface A {
    fun foo() { print("A") }
    fun bar()
}

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

interface C {
    fun foo() { print("C") }
    fun bar(): Int { return 0 }
}

class D: A {
//    인터페이스에서 구현되지 않은 bar 함수를 구현해야 한다.
    override fun bar() {
        print("bar")
    }
}

class E: A, C {
    override fun foo() {
        super<A>.foo()
    }

//    A 와 C 에서 정의된 bar 함수의 리턴타입이 서로 다르기때문에 컴파일 에러가 발생한다.
    override fun bar() {
    }
}

class F: A, B {
//    A와 B 에서 foo 함수가 구현되어 있기때문에 헷갈리지 않게 정확히 어떻게 동작할지를 구현해야 한다.
    override fun foo() {
        super<A>.foo() // A 의 foo 함수를 호출한다.
        super<B>.foo() // B 의 foo 함수를 호출한다.
    }

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