본문 바로가기
공부/이펙티브코틀린

아이템 33 - 생성자 대신 팩토리 함수를 사용하라

by 띵커베르 2024. 7. 11.
728x90

5장 - 객체 생성

코틀린에서는 정적 메서드(static method) 를 사용할 수 없다

 

자바 static method 와의 비교

1. Companion Object

- 클래스와 연결된 싱글톤 객체

class MyClass {
    companion object {
        fun myStaticMethod() {
            println("This is a static-like method")
        }
    }
}

// 사용 방법
MyClass.myStaticMethod()

 

2. Top-Level Functions

- 클래스 외부에 정의된 함수. 정적 메서드처럼 클래스 인스턴스 없이 호출할 수 있다.

fun myTopLevelFunction() {
    println("This is a top-level function")
}

// 사용 방법
myTopLevelFunction()

 

3. Object Declarations

- 싱글톤 객체를 정의한다. 정적 메서드처럼 사용할 수 있다.

object MySingleton {
    fun myMethod() {
        println("This is a method in a singleton object")
    }
}

// 사용 방법
MySingleton.myMethod()

 

더보기

네, 자바의 정적 메서드와 코틀린의 Companion Object는 매우 유사합니다. 둘 다 클래스의 인스턴스 없이 호출할 수 있고, 둘 다 인스턴스 변수에 접근할 수 없습니다. 하지만 몇 가지 차이점이 있습니다. 이를 간단하게 요약해보겠습니다.

자바의 정적 메서드와 코틀린의 Companion Object의 유사점과 차이점

유사점

  1. 클래스 인스턴스와 독립적:
    • 자바: 정적 메서드는 클래스 인스턴스 없이 호출할 수 있습니다.
    • 코틀린: Companion Object의 메서드도 클래스 인스턴스 없이 호출할 수 있습니다.
  2. 인스턴스 멤버 접근 불가:
    • 자바: 정적 메서드는 클래스의 인스턴스 변수나 인스턴스 메서드에 접근할 수 없습니다.
    • 코틀린: Companion Object의 메서드도 클래스의 인스턴스 변수나 인스턴스 메서드에 접근할 수 없습니다.

차이점

  1. 정의 방식과 위치:
    • 자바: static 키워드를 사용하여 클래스 내부에 정적 메서드를 정의합니다.
    • 코틀린: companion object 블록을 사용하여 클래스 내부에 싱글톤 객체를 정의하고 그 안에 메서드를 정의합니다.
  2. 싱글톤 패턴:
    • 자바: 정적 메서드는 기본적으로 싱글톤 패턴과 관련이 없습니다.
    • 코틀린: Companion Object는 기본적으로 싱글톤 패턴을 따릅니다.
  3. 상호 운용성:
    • 코틀린: @JvmStatic 어노테이션을 사용하면 Companion Object의 메서드를 자바에서 정적 메서드처럼 호출할 수 있습니다.

 

 

 


 

  • 팩토리 함수란:
    • 클래스의 인스턴스를 생성하는 함수를 의미합니다. 생성자(constructor)와 달리, 더 구체적인 이름을 가질 수 있고, 다양한 인스턴스 생성 로직을 포함할 수 있습니다.
  • 팩토리 함수의 장점:
    • 더 나은 가독성: 팩토리 함수는 인스턴스를 생성하는 목적을 명확하게 설명하는 이름을 가질 수 있습니다. 예를 들어, createFromJson()과 같은 이름은 무엇을 하는 함수인지 명확히 나타냅니다.
    • 다양한 반환 타입: 팩토리 함수는 동일한 타입의 여러 서브클래스를 반환할 수 있습니다. 생성자는 항상 특정 클래스의 인스턴스를 반환해야 하지만, 팩토리 함수는 다양한 서브클래스나 이미 생성된 인스턴스를 반환할 수 있습니다.
    • 캐싱: 팩토리 함수는 이미 생성된 인스턴스를 반환하여 불필요한 객체 생성을 방지할 수 있습니다.
    • 캡슐화: 객체 생성 로직을 클래스 내부에 캡슐화하여, 클라이언트 코드에서 복잡한 생성 과정을 숨길 수 있습니다.
class MyClass private constructor(val value: Int) {
    companion object {
        // 팩토리 함수
        fun create(value: Int): MyClass {
            return MyClass(value)
        }
    }
}

fun main() {
    val instance = MyClass.create(42)
    println(instance.value)
}

MyClass의 생성자가 private으로 설정되어 직접적으로 인스턴스를 생성할 수 없다.
대신 companion object 내에 create라는 팩토리 함수를 제공하여 인스턴스를 생성한다.

======================================
// 캐싱을 활용한다면..
class MyClass private constructor(val value: Int) {
    companion object {
        // 객체를 캐싱할 맵
        private val cache = mutableMapOf<Int, MyClass>()

        // 팩토리 함수
        fun create(value: Int): MyClass {
            // 캐시에서 객체를 조회, 없으면 생성 후 캐시에 저장
            return cache.getOrPut(value) { MyClass(value) }
        }
    }
}

fun main() {
    val instance1 = MyClass.create(42)
    val instance2 = MyClass.create(42)

    println(instance1 === instance2)  // true, 동일한 객체를 가리킴
}

 

 

 

 

728x90

댓글