• 상속을 하려면 public이나 protected 생성하기 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.
    • private 생성자가 외부에서 접근할 수 없기 때문인데, 상속받은 서브 클래스에서 슈퍼클래스 생성자를 호출할 수 없게 되기 때문이다.
    • 쉽게 말해서 private이기 때문에 상속이 불가능하다
package org.example;

public class Settings {
    private boolean useAutoSteering;
    private boolean useABS;
    private Difficulty difficulty;

    private Settings(){} 기본생성자가 private이기 때문에 상속을 할 수 없다.
    private static final Settings SETTINGS = new Settings();

    public static Settings newInstance(){
        return SETTINGS;
    }

}
  • 정적 팩터리 메서드는 프로그래머가 찾기 어렵다
    • 특히나 문서화(java doc)같은 경우, 생성자는 파라미터가 분명하게 표기되지만 정적 팩터리 메서드는 파라미터가 보이지 않는다.
    • 이럴 땐 아래와 같은 주석 형식을 사용하면 명세서에 설명을 붙일 수 있다.
package org.example;

/**
 * 이 클래스의 인스턴스는 #getInstance()를 통해 사용한다.
 * @see Settings#getInstance()
 */
public class Settings {
    private boolean useAutoSteering;
    private boolean useABS;
    private Difficulty difficulty;

    private Settings(){}
    private static final Settings SETTINGS = new Settings();

    public static Settings getInstance(){
        return SETTINGS;
    }

}
  • 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다. (인터페이스 기반 프레임워크, 인터페이스에 정적 메소드)
    • HelloService 인터페이스의 of 메소드가 이에 해당함. of 메소드의 반환 타입은 **HelloService**이지만 실제로 반환하는 건 **HelloService**를 구현한 **KoreanHelloService**나 **EnglishHelloService**의 인스턴스. 이런 방식은 인터페이스 기반 프레임워크에서 많이 쓰이며, 반환되는 객체의 실제 클래스를 숨기고 인터페이스로만 다루는 방식을 가능하게 함.
  • 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.(EnumSet)
    • **HelloService**의 of 메소드는 입력 매개변수 lang의 값에 따라 다른 클래스(KoreanHelloService 또는 EnglishHelloService)의 인스턴스를 반환함. 이것은 입력 매개변수에 따라 반환되는 객체의 타입이 다를 수 있음을 보여줌.
package org.example;

public interface HelloService {
    public String hello();

    static HelloService of(String lang){
        if(lang.equals("ko")){
            return new KoreanHelloService();
        }else {
            return new EnglishHelloService();
        }
    }
}
  • 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.

3,4 번은 이해가 되는데, 5번은 도통 이해가 되지 않는다.

인강에서는 다른 프로젝트 내에서 ChineseHelloService를 만들어서 ni hao를 출력하게끔 설정했고,

다시 본 프로젝트에서 ChineseHelloService를 import 혹은 의존하지 않고 ni hao를 출력하게 만들었다.

 

강사는 이것이 그저 new ChineseHelloService를 하는것과는 큰 차이가 있다고 하는데, 큰 차이점을 느끼지는 못했다.

언젠간 이해할 수 있겠지

  • 호출될 때마다 인스턴트를 새로 생성하지 않아도 된다(Boolean.valueOf)
    • 기본 생성자로 객체를 생성하면 매번 새로운 인스턴트가 생성된다.
package org.example;

public class Settings {
    private boolean useAutoSteering;
    private boolean useABS;
    private Difficulty difficulty;

    public static void main(String[] args) {
        System.out.println(new Settings());
        System.out.println(new Settings());
        System.out.println(new Settings());
    }
}
---------------------------------------------------------

org.example.Settings@1b6d3586
org.example.Settings@4554617c
org.example.Settings@74a14482

Process finished with exit code 0
  • 정적 팩터리 메서드를 사용하면 인스턴트를 통제할 수 있다.(아래는 싱글 톤 패턴)
    1. Settings 클래스의 접근성: Settings 클래스는 **public**으로 선언되어 있어 어디에서든 접근 가능합니다.
    2. Settings 인스턴스의 생성 제한: Settings 클래스의 생성자는 **private**로 선언되어 있어, 외부에서 직접 Settings 인스턴스를 생성할 수 없습니다.
    3. Settings 인스턴스의 접근 방법: public 메소드인 **newInstance**를 통해 Settings 인스턴스에 접근할 수 있습니다. 이 메소드는 Settings 인스턴스를 반환합니다.
    4. Settings 인스턴스의 동일성 보장: newInstance 메소드는 항상 동일한 Settings 인스턴스(즉, SETTINGS)를 반환합니다. **SETTINGS**는 **final**로 선언되어 있으므로 값이 변경될 수 없습니다.
    5. 싱글턴 패턴의 구현: 위의 모든 조건으로 인해, Settings 클래스의 인스턴스는 항상 하나만 존재하게 됩니다. 이는 싱글턴 디자인 패턴을 구현한 것입니다.
package org.example;

public class Settings {
    // 클래스 내부에서만 접근 가능한 필드들
    private boolean useAutoSteering;
    private boolean useABS;
    private Difficulty difficulty;

    // private 생성자 - 외부에서 이 클래스의 인스턴스를 직접 생성하지 못하게 합니다.
    private Settings(){}

    // 'Settings'의 유일한 인스턴스를 저장하는 필드
    // final로 선언되어 값이 변경되지 않도록 하였습니다.
    private static final Settings SETTINGS = new Settings();

    // 'Settings' 인스턴스를 반환하는 public 메소드
    // 이 메소드를 통해 외부에서 'Settings' 인스턴스에 접근할 수 있습니다.
    // 항상 동일한 인스턴스를 반환하므로, 'Settings' 인스턴스는 항상 하나만 존재하게 됩니다.
    public static Settings newInstance(){
        return SETTINGS;
    }
}

---------------------------------------------------------

Settings settings1 =Settings.newInstance();
        Settings settings2 =Settings.newInstance();

        System.out.println("settings1 = " + settings1);
        System.out.println("settings2 = " + settings2);


---------------------------------------------------------

settings1 = org.example.Settings@1b6d3586
settings2 = org.example.Settings@1b6d3586

Process finished with exit code 0
  • 이름을 가질 수 있다. (동일한 시그니처의 생성자를 두개 가질 수 없다.)
    • 생성자의 시그니처가 중복되는 경우에 정적 팩터리 메서드를 고려하면 좋다
package org.example;

public class Order {
    private boolean prime;
    private boolean urgent;
    private Product product;

    public static Order primeOrder(Product product){
        Order order = new Order();
        order.prime = true;
        order.product = product;
        return order;
    }

    public static Order urgentOrder(Product product){
        Order order = new Order();
        order.urgent = true;
        order.product = product;
        return order;
    }


}

용어 정리

시그니처 : 메서드의 이름, 순서, 파라미터를 묶어 이르는 말. 위 같은 경우에는 컴파일 오류가 발생하지만, 재미있게도 둘 중 하나의 생성자의 파라미터 순서만 바뀌어도 컴파일 오류가 발생하지 않는다.

  • 메서드명, 순서, 타입이 동일 할 때

  • 메서드명, 타입은 동일하나, 순서가 다를 때

+ Recent posts