안드로이드 스튜디오 - 코틀린과 자바의 차이점(4)

Jang Seok Woo·2021년 7월 20일
0

실무

목록 보기
69/136

object

코틀린에서 클래스를 정의하는 키워드는 class인데, object로 클래스를 정의하는 경우도 있다.

2가지 경우에서인데,

  1. 싱글톤

  2. 익명 클래스 객체 생성

먼저 싱글톤에 대해 알아보자면, 인스턴스를 단 하나만 생성하는 방법이다.

자바에서는 static을 이용하여 단 하나의 인스턴스를 만들어낸다. 이유는 메모리 낭비를 방지하는 데 있다.

익명 클래스 객체를 생성하는 이유는 한번만 사용되고 재사용되지 않을 때 사용한다.

싱글톤

코틀린으로 싱글톤 클래스를 정의하면 아래와 같다.

object CarFactory {
    val cars = mutableListOf<Car>()

    fun makeCar(horsepowers: Int): Car {
        val car = Car(horsepowers)
        cars.add(car)
        return car
    }
}

class Car(power: Int) {
}
val car = CarFactory.makeCar(150)
println(CarFactory.cars.size)

자바로 변환을 해보면 아래와 같다.

public final class CarFactory {
   private static final List cars;
   public static final CarFactory INSTANCE;

   public final List getCars() {
      return cars;
   }

   public final Car makeCar(int horsepowers) {
      Car car = new Car(horsepowers);
      cars.add(car);
      return car;
   }

   static {
      CarFactory var0 = new CarFactory();
      INSTANCE = var0;
      cars = (List)(new ArrayList());
   }
}

public static final void main(@NotNull String[] args) {
   Intrinsics.checkParameterIsNotNull(args, "args");
   Car car = CarFactory.INSTANCE.makeCar(150);
   int var2 = CarFactory.INSTANCE.getCars().size();
   System.out.println(var2);
}

위의 자바로 변환된 코드를 보면 CarFactory 객체는 INSTANCE라는 static 객체를 생성합니다. 그리고 이 객체에 접근할 때 CarFactory.INSTANCE를 통해서 접근하게 됩니다. INSTANCE는 static으로 생성되기 때문에 프로그램이 로딩될 때 생성됩니다. 그래서 쓰레드 안전성(thread-safety)이 보장됩니다만, 내부적으로 공유자원을 사용하는 경우 쓰레드 안전성이 보장되지 않기 때문에 동기화(synchronization) 코드를 작성해야 합니다.

companion object

Car 클래스 안에 Factory 패턴을 정의하고 싶을 수 있습니다. Car.makeCar처럼 호출을하는 것이 직관적으로 좋아보여서요. 이럴 때는 companion object로 선언해주면 됩니다.

이런식으로 Car 안에 companion object로 Factory를 정의해주면 Car.makeCar()처럼 호출할 수 있습니다. 사실 Car.Factory.makeCar()로 호출해주는 것이 명시적으로 정확한 표현입니다만, 코틀린은 편의를 위해 Factory를 생략할 수 있게 해주었습니다.

class Car(val horsepowers: Int) {
    companion object Factory {
        val cars = mutableListOf<Car>()

        fun makeCar(horsepowers: Int): Car {
            val car = Car(horsepowers)
            cars.add(car)
            return car
        }
    }
}

fun main(args: Array<String>) {
    val car = Car.makeCar(150)
    val car2 = Car.Factory.makeCar(150)
    println(Car.Factory.cars.size)
}

변환된 자바 코드를 보시면 Car 클래스 안에 중첩클래스(nested class)로 Factory 클래스가 정의되어있습니다. 또한 Car.Factory 클래스는 Factory라는 이름의 static 객체로 선언하였습니다. 외부에서 Car.Factory.makeCar처럼 사용할 수 있는데 여기서 Factory는 클래스 이름이 아니라 static 변수의 이름이었습니다. 코틀린에서 makeCar를 두가지 방식으로 호출했는데요, 자바에서는 동일한 코드로 호출한다는 것을 알 수 있습니다.

public final class Car {
   private final int horsepowers;
   private static final List cars = (List)(new ArrayList());
   public static final Car.Factory Factory = new Car.Factory((DefaultConstructorMarker)null);

   public final int getHorsepowers() {
      return this.horsepowers;
   }

   public Car(int horsepowers) {
      this.horsepowers = horsepowers;
   }

   public static final class Factory {
      @NotNull
      public final List getCars() {
         return Car.cars;
      }

      @NotNull
      public final Car makeCar(int horsepowers) {
         Car car = new Car(horsepowers);
         ((Car.Factory)this).getCars().add(car);
         return car;
      }

      private Factory() {
      }

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

public static final void main(@NotNull String[] args) {
   Intrinsics.checkParameterIsNotNull(args, "args");
   Car car = Car.Factory.makeCar(150);
   Car car2 = Car.Factory.makeCar(150);
   int var3 = Car.Factory.getCars().size();
   System.out.println(var3);
}

익명객체

object는 익명객체를 정의할 때도 사용됩니다. 익명객체는 이름이 없는 객체로, 한번만 사용되고 재사용되지 않을 때 사용합니다. 재사용되지 않기 때문에 귀찮게 클래스 이름을 지어주지 않는 것이죠.

interface Vehicle {
    fun drive(): String
}

fun start(vehicle: Vehicle) = println(vehicle.drive())

아래 코드에서 start()의 인자로 전달되는 object : Vehicle{...}는 익명객체입니다. 이 익명객체는 Vehicle 인터페이스를 상속받은 클래스를 객체로 생성된 것을 의미합니다. 익명객체이기 때문에 클래스 이름은 없고, 구현부는 {...} 안에 정의해야 합니다.

start(object : Vehicle {
    override fun drive() = "Driving really fast"
})

위 코드도 자바로 변환해서 살펴보겠습니다. 자바도 코틀린처럼 Vehicle를 상속한 익명객체를 만들었습니다. (참고로, 코틀린에서 fun start()는 파일의 Top-level에 정의되었기 때문에 자바에서 static 메소드로 생성이 되었습니다.)

public interface Vehicle {
   @NotNull
   String drive();
}

public final class KotlinKt {
   public static final void start(@NotNull Vehicle vehicle) {
      String var1 = vehicle.drive();
      System.out.println(var1);
   }

   public static final void main(@NotNull String[] args) {
      start((Vehicle)(new Vehicle() {
         @NotNull
         public String drive() {
            return "Driving really fast";
         }
      }));
   }
}
profile
https://github.com/jsw4215

0개의 댓글