Companion Object vs Inner Object

들어가며

companon object를 단순히 java의 static을 대체한다고만 하는 경우가 많아 좀 더 자세히 알아보도록 하자.

companon object 에 대한 정의는 공식 문서에서 확인할 수 있다.
공식문서에서는 원리보다는 용례에 대해 중점적으로 설명하고 있는데, 여기에서는 companon object의 원리와 inner object와의 차이에 대해서 알아보고자 한다.

예시코드

class CompanionObject {
    companion object{
        const val LIMIT=100
        fun add(a: Int,b: Int): Int = a+b
    }
}

class InnerObject {
    object Companion{
        const val LIMIT=100
        fun add(a: Int,b: Int): Int = a+b
    }
}

분석

바이트 코드를 살펴보면 static inner 클래스가 생성되는 것은 동일하지만, 인스턴스 선언방식과 상수의 위치 차이를 알 수 있다.

// CompanionObject.java
public final class CompanionObject {
   public static final int LIMIT = 100;
   @NotNull
   public static final Companion Companion = new Companion((DefaultConstructorMarker)null);

   public static final class Companion {
      public final int add(int a, int b) {
         return a + b;
      }

      private Companion() {
      }

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

// InnerObject.java
public final class InnerObject {

   public static final class Companion {
      public static final int LIMIT = 100;
      @NotNull
      public static final Companion INSTANCE;

      public final int add(int a, int b) {
         return a + b;
      }

      private Companion() {
      }

      static {
         Companion var0 = new Companion();
         INSTANCE = var0;
      }
   }
}

자바에서 코틀린 companion object 호출

바이트 코드를 보면 알 수 있듯, 함수는 Companion 클래스 내부에 선언되어 있으므로 직접 호출이 불가능하다.
호출을 하려면 Companion 클래스까지 정의를 해야하는데 이는 java static 메서드의 정의와 상이하다.

import com.example.kotlinspring.companionobject.CompanionObject;

public class CompanionJavaTest {
    public static void main(String[] args) {
        int result = CompanionObject.add(1, 2); // 에러 발생
        int result = CompanionObject.Companion.add(1,2);
    }
}

직접 호출을 하기 위해서는 @JVMStatic 어노테이션을 코틀린에서 정의하면 된다.

class CompanionObject {
    companion object : Factory<CompanionObject>{
        const val LIMIT=100
        @JvmStatic fun add(a: Int,b: Int): Int = a+b
        override fun create(): CompanionObject = CompanionObject()
    }
}

이렇게 하면 아래처럼 바이트 코드에 static 함수가 생기고 이 함수는 Companion 클래스의 함수를 다시 호출한다.

public final class CompanionObject {
    public static final int LIMIT = 100;
    @NotNull
    public static final Companion Companion = new Companion((DefaultConstructorMarker) null);

    @JvmStatic
    public static final int add(int a, int b) {
        return Companion.add(a, b);
    }
    //중략...
}

+ Recent posts