본문 바로가기
  • 개발하는 곰돌이
Programming Language/Kotlin & Java

Kotlin 기본 문법 8 : 정적 변수와 정적 메소드(feat. companion object)

by 개발하는 곰돌이 2022. 12. 14.

목차

    개요

    정적 변수와 정적 메소드는 클래스에 고정된 채로 프로그램이 실행될 때 클래스와 함께 메모리에 적재되어 객체를 생성하지 않고 호출할 수 있는 변수와 메소드를 의미한다. 이러한 정적 변수와 정적 메소드는 해당 클래스의 모든 객체가 공유하여 어디서든 참조가 가능하다. 이 포스트에서는 Kotlin에서 정적 변수와 정적 메소드를 선언하고 다루는 방법에 대해 정리한다.

    Kotlin에는 static 키워드가 없다

    Java에서는 클래스 내부의 변수나 메소드 앞에 static을 붙이기만 하면 정적 변수나 정적 메소드로 선언할 수 있다. 하지만 Kotlin에서는 static이 존재하지 않고 다른 두 가지 방법으로 정적 변수와 정적 메소드를 작성할 수 있다.

    companion object

    Kotlin에서 정적 변수와 정적 메소드를 작성하기 위해선 클래스 내부에 companion object 키워드를 통해 특수한 오브젝트를 생성해야한다. companion object는 단어 그대로 해석하면 동반 객체라는 뜻으로, 쉽게 생각하면 클래스가 메모리에 적재될 때 함께 메모리에 적재되는 유일한 객체라고 볼 수 있을 것이다. 이렇게 companion object 내부에 작성된 변수와 메소드는 Java의 static과 비슷하게 정적 변수와 정적 메소드처럼 사용할 수 있다.

    fun main() {
        println(Test.checkError(100))
        println(Test.checkError(101))
    }
    
    class Test {
        companion object {
            val OK_CODE = 100
            val ERROR_CODE = 101
    
            fun checkError(code: Int) = when (code) {
                OK_CODE -> "OK"
                ERROR_CODE -> "ERROR"
                else -> "UNKNOWN CODE"
            }
        }
    }
    
    /*
    OK
    ERROR
    */

    다만 실제 내부 구조는 Java의 static과는 조금 다르다. 클래스 내부에 companion object를 선언했다는 것은 다시 이야기하면 클래스 내부에 별도의 객체가 존재한다는 것이고, 실제로는 클래스 내부의 이 객체를 통해 변수나 메소드에 접근하게 된다. 또한 이 companion object 역시 하나의 객체만을 갖는 클래스로 간주되기 때문에 이름을 지정할 수 있으며 인터페이스를 구현할 수도 있다. 위 코드를 Java 코드로 디컴파일하면 아래와 같은 결과가 나온다.

    companion object가 포함된 클래스를 Java 코드로 변환하면 Companion이라는 내부 클래스로 구현된다.

    class Test {
        companion object Static : Log {
            val OK_CODE = 100
            val ERROR_CODE = 101
    
            fun checkError(code: Int) = when (code) {
                OK_CODE -> "OK"
                ERROR_CODE -> "ERROR"
                else -> "UNKNOWN CODE"
            }
    
            override fun log() {
                println("로그 찍기")
            }
        }
    }
    
    interface Log {
        fun log()
    }

    이러한 companion object의 이름은 Kotlin에서 호출할 때는 생략할 수 있지만 Java에서 companion object 내부의 변수나 메소드에 접근하려면 클래스에서 companion object를 호출하여 내부의 변수나 메소드에 접근해야 한다. 만약 companion object의 이름을 지정하지 않았다면 기본적으로 Companion이라는 이름이 지정된다.

     

    Java에서 static을 사용할 때처럼 companion object 내부의 멤버에 접근하고 싶다면 Kotlin에서 companion object의 각 멤버 위에 @JvmStatic 어노테이션을 붙여주는 것으로 companion object를 호출하지 않고 클래스에서 바로 정적 멤버와 정적 메소드에 접근할 수 있다.

    @JvmStatic 어노테이션을 붙이면 Java에서 바로 접근 가능하고, 붙이지 않으면 companion object를 호출한 다음 접근해야 한다.

    파일 최상단에 변수와 메소드 작성

    Kotlin에서는 Java와 달리 파일에 클래스를 작성하지 않고 바로 변수와 메소드를 작성할 수 있다. 이렇게 파일 최상단에 작성된 변수와 메소드를 Java 코드로 변환해보면 다음과 같은 결과가 나온다.

    Kotlin의 파일 최상단에 작성된 변수와 메소드를 Java 코드로 변환하면 static 변수와 메소드로 변환된다.

    위 사진을 보면 알 수 있지만 Kotlin에서 파일 최상단에 작성한 변수와 메소드들은 Java에서 '파일명Kt'라는 별도의 클래스가 생성되어 해당 클래스 내부에 정적 변수와 정적 메소드로 나타난 것을 알 수 있다. 실제로 이렇게 Kotlin에서 파일 최상단에 작성한 변수와 메소드를 Java에서 호출할 때는 클래스의 정적멤버를 호출하는 것과 동일한 방법으로 위와 같이 '파일명Kt' 클래스에서 호출할 수 있다.

    Kotlin의 파일 최상단에 작성된 변수와 메소드를 Java에서 호출할 땐 '파일명Kt' 클래스에서 호출할 수 있다.

    이와 같이 Kotlin에서 정적 변수와 정적 메소드를 구현하는 방법은 companion object 내부에 변수와 메소드를 작성하는 것과 파일 최상단에 변수와 메소드를 작성하는 방법으로 나뉜다. 이 두 방법 중 Java의 static에 가까운 방법은 파일 최상단에 작성하는 방법으로, 일반적으로 유틸성 함수를 작성할 때는 파일 최상단 메소드로 작성하는 것이 권장된다고 한다.

    Kotlin의 싱글톤(feat. object)

    싱글톤은 어떤 클래스의 객체를 프로그램에서 단 하나만 생성하여 사용할 수 있도록 구현하는 패턴이다. Java에서 싱글톤을 구현하는 방법은 아래 링크를 참조하면 좋을 것이다.

     

    자바 싱글톤 패턴의 변화 (다양한 싱글톤 패턴 구현 방법)

    목차 싱글톤 패턴은 어떠한 클래스에 대한 객체(=인스턴스)를 해당 프로그램에서 단 하나만 가지도록 강제하는 구현 패턴 입니다. 단 하나만 있지 않으면 문제가 생기거나, 하나만 있어도 문제

    nahwasa.com

    Kotlin에서는 object라는 키워드를 제공한다. 이 키워드로 클래스를 작성하면 자동적으로 싱글톤 패턴이 구현된 클래스가 작성된다. 이 때 object로 작성된 싱글톤 클래스는 생성자를 가질 수 없다는 점에 주의하자. 이후 객체를 받아서 사용할 때는 클래스의 이름만 작성해주면 된다.

    fun main() {
        val singleton = Singleton
        println(singleton.checkError(100))
    }
    
    object Singleton {
        val OK_CODE = 100
        val ERROR_CODE = 101
    
        fun checkError(code: Int) = when (code) {
            OK_CODE -> "OK"
            ERROR_CODE -> "ERROR"
            else -> "UNKNOWN CODE"
        }
    }
    
    /*
    OK
    */

    마무리

    Kotlin에서 정적 변수와 정적 메소드를 다루는 방법에 대해서 정리해보았다. Kotlin에서 정적 변수와 정적 메소드를 작성하려면 companion object를 사용하는 방법과 파일 최상단에 작성하여 사용하는 방법으로 나뉘어지는데, 이 중 후자의 방법이 Java의 static에 좀 더 근접한 방법이라는 것을 알 수 있다.

     

    다음 포스트에서는 Kotlin의 다양한 클래스에 대해 정리하고자 한다.

     

    댓글 피드백은 언제나 환영합니다.

     

    댓글