[코틀린] Singleton

Java -> kotlin으로 전환하던 와중에 싱글턴 형태에 대해서 의문이 들어 정리해본다.

Pettern 1

getInstance()해서 호출하는 형태로 기존에 자바에서 많이 사용하던 패턴이다. 가장 익숙한 패턴이고 lazy하게 생성할 수 있도록 형태를 재가공했는데, 그러다보니 자바로 생성되는 코드들이 양이 많다.

class TempClass {
    companion object {
        private val INSTANCE : TempClass by lazy { TempClass() }

        @JvmStatic
        fun getInstance() = INSTANCE
    }
    fun getTempName()  : String{
        return "Temp"
    }

    fun getTempNumber() : Int{
        return 1
    }
}
//Decompiled
public final class TempClass {
   private static final Lazy INSTANCE$delegate;
   public static final TempClass.Companion Companion = new TempClass.Companion((DefaultConstructorMarker)null);

   @NotNull
   public final String getTempName() {
      return "Temp";
   }

   public final int getTempNumber() {
      return 1;
   }

   static {
      INSTANCE$delegate = LazyKt.lazy((Function0)null.INSTANCE);
   }

   @JvmStatic
   @NotNull
   public static final TempClass getInstance() {
      return Companion.getInstance();
   }

   @Metadata(
      mv = {1, 1, 13},
      bv = {1, 0, 3},
      k = 1,
      d1 = {"\u0000\u0014\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0018\u0002\n\u0002\b\u0006\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\b\u0010\t\u001a\u00020\u0004H\u0007R\u001b\u0010\u0003\u001a\u00020\u00048BX\u0082\u0084\u0002¢\u0006\f\n\u0004\b\u0007\u0010\b\u001a\u0004\b\u0005\u0010\u0006¨\u0006\n"},
      d2 = {"LTempClass$Companion;", "", "()V", "INSTANCE", "LTempClass;", "getINSTANCE", "()LTempClass;", "INSTANCE$delegate", "Lkotlin/Lazy;", "getInstance", "LeetCodes"}
   )
   public static final class Companion {
      // $FF: synthetic field
      static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(TempClass.Companion.class), "INSTANCE", "getINSTANCE()LTempClass;"))};

      private final TempClass getINSTANCE() {
         Lazy var1 = TempClass.INSTANCE$delegate;
         KProperty var3 = $$delegatedProperties[0];
         return (TempClass)var1.getValue();
      }

      @JvmStatic
      @NotNull
      public final TempClass getInstance() {
         return ((TempClass.Companion)this).getINSTANCE();
      }

      private Companion() {
      }

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

Pettern 2

object를 사용한는 형태로 kotlin에서 사용하라고 권장하는 방법이다.

object TempClass {
    var tempCount = 0
    @JvmStatic
    fun getTempName()  : String{
        return "Temp"
    }

    @JvmStatic
    fun getTempNumber() : Int{
        tempCount++
        return 1
    }
}

//Decompiled

public final class TempClass {
   private static int tempCount;
   public static final TempClass INSTANCE;

   public final int getTempCount() {
      return tempCount;
   }

   public final void setTempCount(int var1) {
      tempCount = var1;
   }

   @JvmStatic
   @NotNull
   public static final String getTempName() {
      return "Temp";
   }

   @JvmStatic
   public static final int getTempNumber() {
      int var10000 = tempCount++;
      return 1;
   }

   private TempClass() {
   }

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


뭐가 더 좋아?

음.. 결론 부터 얘기하면 Object가 미세하지만 더 좋다.

getInstance형태와 마찬가지로 object도 lazy init을 하기 때문에 메모리 상의 차이점은 없고, getInstnace형태가 코드 생성되는 양도 많고, 불필요한 코드이며 단순히 getInstance형태가 익숙하다는 것 뿐이다.

다만 getInstance를 사용할 때 이점은 현재는 Java와 kotlin 코드가 섞여있어서 매 메서드마다 @JvmStatic을 붙여야하니 귀찮고, getInstance -> object로 바꾸려면 다른 클래스들에 건들이는 것들도 많아지니 컨플릭을 최소화 하기 위해서 일단 둘다 용인하는 형태로 고고!