프로그래밍 언어/Kotlin

Kotlin - Interface

IOT_AN 2024. 2. 2. 13:58

안드로이드 스튜디오 개발자 페이지를 기반으로 작성됨.

https://developer.android.com/courses/pathways/android-basics-compose-unit-1-pathway-1?hl=ko

 

Kotlin 프로그래밍 소개  |  Android Basics Compose - First Android app  |  Android Developers

Kotlin에서 Android 앱 빌드를 준비하기 위해 Kotlin의 입문 프로그래밍 개념을 알아봅니다.

developer.android.com

 

이번 글에서는 객체지향 프로그래밍에서 클래스와 더불어 굉장히 자주 활용되는 Interface에 대해서 소개해보도록 한다.

 

 


 

Interface

 

Interface라는 것이 무엇일까?

위키 백과에 검색해보면, 다음과 같이 설명되어 있다.

인터페이스는 서로 다른 두 개의 시스템, 장치 사이에서 정보나 신호를 주고받는 경우의 접점이나 경계면이다.

 

필자가 지금까지 이해한 인터페이스는 구현 정보가 들어 있지 않은 설계도라고 생각한다. 또 다른 말로는 사용자가 애플리케이션을 활용할 때 직접적으로 맞닿는 부분이지만 세부적인 내용은 포함되어 있지 않은 개념이라고도 설명할 수 있겠다.

 

예시를 들어서 설명해보자.

 

우리가 스마트폰을 쓰는데, 홀드 키를 누르거나 디스플레이를 두 번 터치하여 스마트폰을 키게 된다. 하지만 우리는 세부 동작 방식에 대해서는 알 수가 없다.

 

또한 버튼의 종류에 따라서 스마트폰을 킬 수도 있고, 볼륨을 크게 하거나 작게 하거나 다양한 역할을 수행할 수도 있다.

 

위의 예시처럼 인터페이스는 완전 추상화 개념이 적용된 다형성을 위해 만들어진 객체이라고 이해할 수 있다.

 

클래스를 활용하여 스마트폰 버튼을 객체화 시킨다고 하면, 다른 종류의 버튼에 대해서 각각의 function을 정의하기 힘들 수 있다. 공통점이 없을 경우 그럴 것이다.

 

이러한 경우 인터페이스를 활용하여 버튼이 가질 수 있는 설계 요소를 정의해두고 종류에 따라서 각 버튼의 설계도를 구현하는 클래스를 만들 수 있을 것이다.

 

Android Developer 페이지에서는 다음과 같이 Progress Interface를 구현해두고, Quiz, Survey, Recipe 등의 클래스로 구현할 수 있다는 예시로 설명해주고 있다. (사실 이 예시가 좀 더 적당한 것 같다 ㅋㅋㅋ)

 

Interface - class 관계도

 

이제 인터페이스를 어떻게 활용하는지 코드 예시로 한번 알아보자.

 

// interface just drawing 
interface ProgressPrintable {
    val progressText: String
    fun printProgressBar()
}

// class implements interface
class Quiz: ProgressPrintable {
    override val progressText: String
        get() = "${answered} of ${total} answered"
    
    override fun printProgressBar() {
        repeat(Quiz.answered) { print("▓") }
        repeat(Quiz.total - Quiz.answered) { print("▒") }
        println()
        println(progressText)
    }
    
    companion object {
        val total: Int = 10
        val answered: Int = 3
    }
}

fun main() {
    val quiz: ProgressPrintable = Quiz()
    quiz.printProgressBar()
}

 

- 수행 결과

▓▓▓▒▒▒▒▒▒▒
3 of 10 answered

 

 

기본 설계도인 ProgressPrintable 인터페이스를 정의해주고, 그에 따른 개별의 클래스인 Quiz 클래스를 통해 인터페이스를 구현해준 모습이다. 기존 상속 개념과 같이 구현해야 하는 메서드 혹은 속성들은 override 키워드를 통해 처리해주면 된다.

 

인터페이스를 사용했을 때 이점들은 다음과 같다.

  • 종속 항목 수동 삽입: 종속 항목(메서드 혹은 함수에 들어가는 클래스 타입의 매개변수)의 모든 속성과 메서드를 정의하는 인터페이스를 만듦으로써, 종속 항목에 들어가는 여러 구현체를 테스트 해보고 실제 구현에 알맞은 클래스로 선택할 수 있다.
  • Compose Multiplatform: 구현이 platform마다 다를 경우에 인터페이스로 종속 항목을 만들고 platform에 따른 구현체를 넘겨줌으로써 Multiplatform application을 만들 수 있다.
  • 위와 같은 이유로 코드의 재사용성이 증가하고 객체 지향 프로그래밍의 의미를 더욱 살릴 수 있다. 또한 다중 상속을 통한 객체간 연결성을 향상 시킬 수 있다.

 

  • 참고 -  객체 지향 언어에서의 추상 클래스와 인터페이스의 차이점

추상 클래스와 인터페이스는 태생적으로 다른 개념이라고 소개한다.

 

추상 클래스의 경우,

공통점이 있는 서브 클래스들이 슈퍼 클래스의 추상 메서드를 통해 클래스를 확장하고, 서브 클래스만의 특징을 추상 메서드를 통해 구현하기 위해서 존재한다.

 

인터페이스의 경우,

공통점이 없는 클래스들이 인터페이스의 추상 메서드를 구현하여 껍데기를 채우려고 하기 위해서 존재한다. 정확히는 메서드 구현부의 공통점이 없는 클래스들이라고 지칭하는 것이 올바를 수 있다.

 

  • Kotlin 에서의 인터페이스의 특징

객체지향 언어로 가장 잘 알려진 JAVA의 경우 인터페이스 안에서 구현부를 포함해두지 않는다.

하지만 Kotlin에서의 인터페이스는 구현부를 옵션으로 허용한다. 공식 문서를 봐도 이유는 써져 있지 않아서 왜 그렇게 둔지는 모르겠으나, 뭔가 Kotlin이 함수를 굉장히 중요시하는 함수형 프로그래밍 언어라서 그런가... 싶긴 하다.

근데 또 속성도 val로 선언했을 경우 getter를 통해 값을 저장하는 것은 가능하다.

 

여튼 Kotlin에서는 추상클래스와 인터페이스 간 차이점은 Property의 초기화 가능 여부이다. var로 선언한 변수에 대해서 인터페이스에서는 초기화가 불가능하고 추상 클래스에서는 초기화가 가능하다. 나머진 모두 동일한... 개념이라고 생각해둘 수 있다.

 

사실 Kotlin에서 다루는 추상 클래스나 중첩 클래스 등 클래스의 여러 용도에 대해서는 아직 다루지 않았는데, 시간적 여유가 생기거나 Kotlin 언어에 대해서 따로 공부할 수 있다면 따로 소개해보도록 하겠다.