[자바/코틀린] companion object에 대해

Lee Yongin·2023년 2월 23일
2

자바/코틀린

목록 보기
2/5
post-thumbnail

Companion object

클래스가 메모리에 올라갈 때, 동시에 companion object가 인스터스로서 힙에 올라간다 하여 '동반 객체'라고 한다.
companion object 정보를 찾다보면 '자바의 static 개념이 아니다' 이런 말이 많이 있다. 그만큼 필자도 헷갈렸다. 일단 자바의 static개념이 무엇인지 확인도 해보았다.

'Static'은 무엇을 말하는 건가

자바와 코틀린은 모두 JVM(Java Virtual Machine)에서 실행된다. JVM의 Method Area, Heap Area가 이 글의 포인트이다.

Method Area

클래스에 대한 정보와 함께 클래스 변수(static variable)가 저장되는 곳이다.
런타임 상수 풀, 멤버 변수, 클래스 변수, 생성자 및 메소드를 저장한다.
이 곳에 저장되는 정보가 static한 정보이다.

Heap Area

반면 Heap Area는 동적으로 할당되는 정보가 있는 곳이다. 흔히 말하는 붕어빵으로 찍어낸 붕어빵들이 생성되기도하고, 가비지 컬렉팅되어 없어지기도 한다.

Companion object

object키워드와의 연관성

companion object는 코틀린의 object키워드와 연관이 있다. object키워드는 설명할 것이 많지만, 이 글에서 언급할 포인트는 객체 선언 기능이다.
object 키워드는 자바의 static 키워드와 new 키워드를 대신한다. 클래스 정보 적재와 객체 선언이 동시에 이루어진다.
예를 들어 아래와 같이 object키워드로 Datasource를 선언하면, Datasource클래스를 정의함과 동시에 해당 인스턴스를 생성한다. 앞에 나온 Method Area, Heap Area에 동시에 정보를 적재한다는 뜻이다.

object Datasource {
    var baseUrl: String = "todolist-dev.leeyongin.com/"

    fun printBaseUrl) {
        println("baseUrl is : $baseUrl")
    }
}

companion object도 object키워드와 같은 역할을 한다. 다만 차이점은 2가지다.

object키워드와의 차이점

  1. '클래스 내부에 있는 object'를 굳힌 형태인 것이다.
  2. companion object 내부에 있는 변수,메소드에 대한 접근을 더 자연스럽게 보이도록 눈속임했다.

예제1 처럼 클래스 내부에 object를 선언할 수 있다.따라서 접근1 처럼 DataSource 객체의 변수에 접근한다.

예제1

class DataSourceClass {
    object Datasource {
      var baseUrl: String = "todolist-dev.leeyongin.com/"

      fun printBaseUrl() {
          println("baseUrl is : $baseUrl")
      }
  }
}

접근1

DataSourceClass.Datasource.baseUrl

예제2

예제1은 아래처럼 companion object로 바꿔줄 수 있다. 이로 인해 클래스 내부 객체에 접근할 때 해당 객체의 이름을 반복적으로 타이핑할 필요가 없다.

class DataSourceClass {
    companion object {
      var baseUrl: String = "todolist-dev.leeyongin.com/"

      fun printBaseUrl() {
          println("baseUrl is : $baseUrl")
      }
  }
}

접근2

DataSourceClass.baseUrl

+companion object는 클래스 당 1개로 제한되기 때문에 companion object에 이름을 붙여도되고 안 붙여도 된다.

그럼 object키워드와 내부적으로는 어떻게 다른가?

아래 그림과 같이 자바와 코틀린은 모두 java byte-code로 변환되어 실행된다. 이번엔 java byte-code로 변환한 코드를 비교해보자.

위에서 보인 코드1과 코드2를 java byte-code로 변환하면 아래와 같다.
2가지를 눈여겨 보았다.

1.object 또는 companion object로 선언된 객체클래스의 할당이 어디서 이루어지는가?
2.static하게 사용하고 싶은 object 또는 companion object의 변수, 메소드는 어디서 static으로 선언되는가?

object 키워드를 사용하면 객체 내 static{} 에서 new를 이용한 객체생성과 변수 할당이 이루어진다.

public final class DataSourceClass {
  @Metadata(...중략...)
  public static final class Datasource {
        @NotNull
        private static String baseUrl;
        @NotNull
        public static final DataSourceClass.Datasource INSTANCE;

        ...중략...

        static {
           DataSourceClass.Datasource var0 = new DataSourceClass.Datasource();
           INSTANCE = var0;
           baseUrl = "todolist-dev.leeyongin.com/";
      	}
   }
}

하지만 companion object를 사용하면 아래와 같이 new를 이용한 객체생성과 변수 할당이 객체의 외부, 클래스의 내부에서 진행된다. 자바 개발이었다면 위의 패턴과 아래의 패턴을 구분해서 사용해야했겠지만 코틀린은 이걸 object와 companion object로 정형화했다.

public final class DataSourceClass {
   @NotNull
   private static String baseUrl = "todolist-dev.leeyongin.com/";
   @NotNull
   public static final DataSourceClass.Datasource Datasource = new DataSourceClass.Datasource((DefaultConstructorMarker)null);

   @Metadata(...중략...)
   public static final class Datasource {
      ...중략...

      // $FF: synthetic method
      public Datasource(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

마무리하며

object 키워드는 이 글에서 언급한 객체 선언 기능 외에도 설명것들이 많다. 팩토리 생성 및 싱글톤 패턴과 관련되어 설명하는 자료들이 많으니 더 공부하면 좋겠다.

참고자료

companion object - 자바의 static과 같은 것인가?
object 키워드
object& companion object
jvm의 메모리 사용 방식

profile
f1을 좋아하는...🏆 f1처럼 빠르고 정확한 걸 좋아하는 안드로이드 개발자

0개의 댓글