코틀린 (Kotlin) 인터페이스 정리

인터페이스

코틀린의 인터페이스는 자바와 같이 interface 로 시작합니다.

interface Clickable {
    fun click()
}

interface 를 구현할 때는 [클래스: 인터페이스] 형식으로 구현됩니다.
extends(확장)과 implements(구현) 둘 다 :(콜론) 으로 구현합니다.

코틀린은 1개의 상속과 여러개의 인터페이스를 구현할 수 있습니다.
그리고 override 변경자를 사용해서 자바의 @Override 를 대신합니다.
만약에 override 된 함수 이름과 다른 함수 이름이 중복될경우 둘중 하나는 다른 이름을 사용해야 됩니다.

//클래스 상속이나 구현은 : 으로 구분
class Button : Clickable, View { //Clickable은 interface, View 는 상속
    override fun click() { 
        //구현시 override 필수
    }
}

디폴트 메소드

자바8 에서는 디폴트 메소드인경우 default 를 붙이지만 코틀린에서는 따로 추가되는 변경자가 없이 함수를 구현하면 됩니다.

interface Clickable {
    fun click()
    fun showOff() = Log.d("Clickable showoff") //디폴트 메소드
}

디폴트 메소드는 override 를 하지않은 경우 디폴트로 사용되는 함수입니다.

같은 함수 이름을 갖는 interface

디폴트 함수 이름이 같은 두개의 인터페이스를 구현해야되는 경우를 생각해봅니다.
디폴트 함수 이름이 같은 이름인 인터페이스를 정의하면 컴파일 에러가 발생합니다.

interface Clickable {
    fun click()
    fun showOff() = Log.d("Clickable showoff")
}

interface Focusable {
    fun click()
    fun showOff() = Log.d("Focusable showoff")
}

class Button : Clickable, Focusable //컴파일 에러
    ...
}

이를 해결하기 위해서는 하위 클래스에서 super() 를 호출해야 됩니다.
showOff() 를 override 하고 블럭 안에서 super<...> 를 호출해서 상위 클래스의 메소드를 지정할 수 있습니다.

class Button : Clickable, Focusable {

    //같은 이름의 메소드 override
    override fun showOff() {
        super<Clickable>.showOff()  // 상위 함수 호출시 super<..> 를 사용할 수 있다
    
    super<Focusable>.showOff()
    }

    override fun click() { //구현시 override 필수
    
    L.d("override click()")
    }

}

어떤 디폴트 메소드를 실행할지 컴파일러가 판단하기 어렵기때문에 super<...> 으로 직접 지정해야 됩니다.
Clickable 의 showOff() 만 호출할 수도 있습니다.

class Button : Clickable, Focusable {
    override fun showOff() {
        super<Clickable>.showOff() //Clickable 만 호출 가능
    }

    override fun click() {
    
    L.d("override click()")
    }
}

디폴트 메소드가 있는 코틀린을 자바에서 사용할 경우 디폴트 메소드를 사용할 수 없기때문에 직접 구현해야 됩니다.
코틀린은 자바6 을 기반으로 하기때문입니다.

인터페이스에 프로퍼티 선언하기

코틀린에서는 인터페이스에 프로퍼티를 선언할 수 있습니다.

interface User {
    //인터페이스의 프로퍼티 선언
    //실제로는 getNickname() {} 이라는 비어있는 함수만 생성 됨
    val nickname: String
 
}

//주생성자에서 프로퍼티 override 로 구현하기
//구현을하면 nickname 프로퍼티가 생성되고 생성자에서 할당이 됨
//getNickname() { return nickname } 이 자동 생성 됨
class PrivateUser(override val nickname: String) : User

주 생성자에서 인터페이스 프로퍼티를 override 하면 비어있던 interface 의 getNickname() 이 자동으로 구현됩니다.
PrivateUser 에 nickname 프로퍼티가 생성이되고 생성자로 넘겨받은 파라메터로 this.nickname = nickname 처럼 값이 할당됩니다.
그리고 getNickname() { return nickname } 으로 getter() 가 자동 생성됩니다.

커스텀 게터로 인터페이스 프로퍼티 구현하기

커스텀 게터로 interface 의 프로퍼티를 구현할 수 있습니다.

//커스텀 게터로 프로퍼티 override
class SubscribingUser(val email :String) : User {

    override val nickname: String
        get() = email.substringBefore("@") //nickname 을 호출할때마다 계산 됨
}

SubscribingUser 클래스에 email 프로퍼티가 생성되고 User 인터페이스의 getNickname() 함수를 오버라이드해서 커스텀 get() 으로 email 문자열에서 nickname을 추출하여 리턴합니다.
그리고 getNickname() 은 호출할 때마다 계산 됩니다.

프로퍼티 초기화로 인터페이스 프로퍼티 구현하기

인터페이스를 상속받은 경우 인터페이스 프로퍼티를 초기화 식으로도 사용할 수 있습니다.

//프로퍼티 초기화 식으로 override
class FacebookUser(val email :String) : User {

    //프로퍼티 초기화는 한번만 할당되고 그 후로는 저장된 값을 사용
    override val nickname: String = email.substringBefore("@")
}

커스텀 게터로 인터페이스 프로퍼티를 구현하는 것과 프로퍼티 초기화로 구현하는 것에는 차이점이 있습니다.

  • 커스텀 게터 : 매번 식을 수행
  • 프로퍼티 초기화 : 최초 한번만 식이 실행되고 이후에는 저장된 값을 사용

인터페이스 프로퍼티를 상속한 경우

인터페이스 프로퍼티에 커스텀 get() 을 구현하면 상속받은 하위 클래스에서는 구현할 필요가 없습니다.

프로퍼티만 있는 경우 하위 클래스에서 get()을 override 해서 구현해야 합니다.

인터페이스에서는 프로퍼티 초기화를 할 수 없습니다.
왜냐하면 값이 할당되지 않게 때문입니다.

interface User {
    val email:String //구현시 오버라이드 필요

    val nickname: String //구현시 오버라이드 불필요
        get() = email.substringBefore("@")

    //error!!! 인터페이스에서는 초기화 할수 없음
    val emailId :String= email.substringBefore("@")
}

//email은 꼭 오버라이드 해야됨!!
class UserChild(override val email: String) : User




*개인적으로 코틀린을 공부하면서 정리한 자료입니다. 수정 사항 및 이슈가 있는 경우 메일 부탁드립니다.

댓글

  1. 초반부 주석에 View는 상속이라 써있는데 View 또한 인터페이스 아닌가요?

    답글삭제

댓글 쓰기

이 블로그의 인기 게시물

코틀린 (Kotlin) filter, map, all, any, count, find, groupBy, flatMap 함수 정리

RecyclerView 에서 notifyItemChanged()의 payload 이해하기