본문 바로가기
이펙티브 자바

정적 팩터리 메서드

by 이상한나라의개발자 2023. 12. 20.

이펙티브 자바(Effective Java) 에서 언급하는 정적 팩토리 메서드 (static factory method)는 객체 생성을 위한 디자인 패턴의 일종 입니다. 이 방법은 생성자 대신 클래스 내부에 정적 메서드를 사용하여 객체를 반환합니다.

 

  1. 명시적 이름 사용 : 생성자에 비해, 정적 팩토리 메서드는 반환될 객체의 특성을 잘 설명하는 이름을 가질 수 있습니다. 예를 들어 'Order.createInstance' 은 이름만으로도 새로운 인터스턴스를 반환하는 것을 알 수 있습니다.
  2. 호출될 때마다 새로운 객체를 생성하지 않아도 됨 : 필요한 경우, 정적 팩토리 메서드는 항상 새로운 객체를 생성하지 않고, 미리 생성된 객체를 반환할 수 있습니다. 이는 메모리 사용 최적화에 도움을 줍니다.
  3. 반환 타입의 하위 타입 객체를 반환할 수 있음 : 반환 타입이 인터페이스 일 경우 메서드는 다양한 구현 클래스의 객체를 반환할 수 있어 유연성이 높아 집니다.
  4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있음 : 정적 팩토리 메서드는 입력 매개변수에 따라 다른 클래스의 객체를 반환 할 수 있어 반환 타입의 클래스가 여러 구현을 가질 수 있습니다.
  5. 반환할 객체의 클래스가 존재하지 않아도 됨 : 정적 팩토리 메서드가 인터페이스나 추상 클래스 반환 타입으로 사용하고, 실제 구현 클래스는 메서드가 호출되는 시점에 결정될 수 있음을 의미합니다.
  6. 메서드 체인을 형성하는데 유용함 : 이러한 메서드는 자신의 클래스에 대한 메서드 호출을 체은으로 연결할 수 있어, 코드를 더 읽기 쉽고 간견하게 만듭니다. ( 이부분은 빌더패턴에서 기록 합니다 ) 
  7. 단점 

    • 하위 클래스를 만들기 어렵다.
    • 다른 정적 메서드와 구별하기 어렵다.
    •  API 문서에 생성자와 구분되지 않아 사용자가 찾기 어렵다.

 

장점

 

1. 명시적 이름 사용

public class Rectangle {

    private final double width;
    private final double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    public static Rectangle createSquare(double side) {
        return new Rectangle(side, side);
    }

    public static Rectangle createWithAspectRatio(double width, double aspectRatio) {
        double height = width / aspectRatio;
        return new Rectangle(width, height);
    }

    @Override
    public String toString() {
        return "Rectangle{" +
                "width=" + width +
                ", height=" + height +
                '}';
    }

    // main 메서드 추가
    public static void main(String[] args) {
        // 정사각형 생성
        Rectangle square = Rectangle.createSquare(5);
        System.out.println("Square: " + square);

        // 종횡비가 2:1인 직사각형 생성
        Rectangle rectangle = Rectangle.createWithAspectRatio(10, 2);
        System.out.println("Rectangle with aspect ratio: " + rectangle);
    }
}

 

createSquare 정적 팩토리 메서드는 한변의 길이를 인자로 받아 정사각형을 생성합니다. 메서드 이름만 봐도 정사각형이 생성될 것임을 쉽게 알수 있습니다.

 

createWithAspectRatio 정적 팩토리 메서드는 너비와 종횡비(aspect ratio)를 인자로 받아, 해당 비율에 맞는 직사각형을 생성합니다.

이 메서드도 이름을 통해 생성되는 직사각형의 특성을 명확하게 알 수 있습니다.

 

이렇게 명시적인 이름을 사용하는 정적 팩토리 메서드는 코드의 가독성을 옾이고 생성자의 의도를 더 분명하게 전달합니다.

 

2. 호출될 때마다 새로운 객체를 생성하지 않아도 됨 

이러한 특성은 인스턴스를 재사용하여 메모리 사용을 줄일 수 있습니다(Heap 영역). 대표적인 예로 싱글톤 패턴이나 불변 객체의 캐싱이 있습니다.

 

public class Singleton {

    private static Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
	
    public void doSomething() {
        System.out.println("doSomething");
    }

    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();

        if ( singleton1 == singleton ) {
            System.out.println("같은 객체입니다." + singleton + " == " + singleton1);
        } else {
            System.out.println("다른 객체입니다.");
        }
        singleton1.doSomething();
    }
}

 

private 접근제어자를 사용하여 싱글톤 객체의 외부 생성을 막습니다.  잠시 접근제어자에 대해 간략하게 알아볼께요

 

  • public : 모든 외부 호출을 허용한다.
  • default : (package-private) 같은 패키지 안에서 호출은 허용한다.
  • protected : 같은 패키지 안에서 호출을 허용하고, 패키지가 달라도 상속 관계에서의 호출은 허용한다.
  • private : 모든 외부 호출은 막는다.

getInstance 정적 팩토리 메서드를 통해 싱글톤 인스턴스에 접근할 수 있습니다. 이 메서드는 클래스의 인스턴스가 아직 생성되지 않았다면 새로 생성하고, 이미 생성 되었다면 기존 인스턴스를 반환 합니다. 

 

해시코드 값 비교하면 아래와 같이 같은 해시코드의 값을 반환 하게 됩니다.

실행 결과 : 같은 객체입니다.staticfactorymethod.Singleton@27716f4 == staticfactorymethod.Singleton@27716f4

 

3. 반환 타입의 하위 타입 객체를 반환할 수 있음

인터페이스를 반환 타입으로 사용할때 유용하며, 클라이언트는 인터페이스만을 알고 있스면 됩니다. 실제 반환되는 객체의 구체적인 클래스 타입은 메서드 내부에서 결정됩니다. 이런 접근 방식은 API를 더 유연하게 만들고 내부 구현은 숨길 수 있습니다.

 

// 인터페이스 정의
public interface Animal {
    void makeSound();
}

// 인터페이스 구현: Dog 클래스
public class Dog implements Animal {
    @Override
    public void makeSound() {
        System.out.println("멍멍!");
    }
}

// 인터페이스 구현: Cat 클래스
public class Cat implements Animal {
    @Override
    public void makeSound() {
        System.out.println("냐옹!");
    }
}

// Animal 객체를 생성하는 팩토리 클래스
public class AnimalFactory {
    // Dog 객체를 반환하는 정적 팩토리 메서드
    public static Animal createDog() {
        return new Dog();
    }

    // Cat 객체를 반환하는 정적 팩토리 메서드
    public static Animal createCat() {
        return new Cat();
    }

    // main 메서드
    public static void main(String[] args) {
        Animal dog = AnimalFactory.createDog();
        Animal cat = AnimalFactory.createCat();

        dog.makeSound(); // 출력: 멍멍!
        cat.makeSound(); // 출력: 냐옹!
    }
}

 

 

이러한 접근 방식은 클라이언트 코드가 구체적인 클래스 타입에 대해 알 필요 없이 인터페이스를 통해 다양한 구현체를 사용할 수 있게 해줍니다. 또한, 나중에 Animal 인터페이스의 새로운 구현 클래스를 추가하더라도, 팩토리 메서드를 수정하여 쉽게 새로운 구현체를 반환 하도록 할 수 있습니다.

 

4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있음

제목 그대로 인스턴스를 생성할 때 매개 변수의 값에 따라 다른 인스턴스를 생성할 수 있음을 의미합니다. 특히 팩토리 메서드가 다양한 구현체를 갖는 인터페이스나 추상 클래스를 반환하는 경우에 유용합니다.

 

// 인터페이스 정의
public interface Vehicle {
    void move();
}

// 인터페이스 구현: Car 클래스
public class Car implements Vehicle {
    @Override
    public void move() {
        System.out.println("Car is moving");
    }
}

// 인터페이스 구현: Bicycle 클래스
public class Bicycle implements Vehicle {
    @Override
    public void move() {
        System.out.println("Bicycle is moving");
    }
}

// Vehicle 객체를 생성하는 팩토리 클래스
public class VehicleFactory {
    // 입력 매개변수에 따라 다른 클래스의 객체를 반환하는 정적 팩토리 메서드
    public static Vehicle createVehicle(String type) {
        if ("car".equalsIgnoreCase(type)) {
            return new Car();
        } else if ("bicycle".equalsIgnoreCase(type)) {
            return new Bicycle();
        } else {
            throw new IllegalArgumentException("Unknown vehicle type");
        }
    }

    // main 메서드
    public static void main(String[] args) {
        Vehicle car = VehicleFactory.createVehicle("car");
        Vehicle bicycle = VehicleFactory.createVehicle("bicycle");

        car.move(); // 출력: Car is moving
        bicycle.move(); // 출력: Bicycle is moving
    }
}

 

 

5. 반환할 객체의 클래스가 존재하지 않아도 됨

위 내용은 서비스 제공자 프레임워크 (Service Provider Framework) 의 근간이 됩니다. 자바에서는 이미 서비스 제공자 클래스 ServiceLoader.load(xxxx.class) 를 제공 하고 있습니다. 또한, 대표적인 서비스 제공자 프레임워크로는 JDBC(Java Database Connectivity) 가 있습니다. 

 

  • 서비스 제공자 프레임워크에서의 제공자는(provider)는 서비스의 구현체이다.
  • 그리고 이 구현체들은 클라이언트에 제공하는 역할을 프레임워크가 통제하여 클라이언트를 구현체로 부터 분리해준다
  • 서비스 제공자 프레임워크는 크게 3가지 핵심 컴포넌트로 이루어진다.

    • 구현체의 동작을 정의하는 서비스 인터페이스
    • 제공자가 구현체를 등록 할 때 사용하는 서비스 제공자 인터페이스 (제공자 등록 API, provider registration API) 
    • 서비스 관리자 (서비스 접근 API)

그럼 위 3가지 핵심 컴포너트를 기준으로 서비스 제공자 프레임워크를 구성

 

// 구현체의 동작을 정의하는 서비스 인터페이스
public interface Service {
    void execute();
}


// 제공자가 구현체를 등록 할 때 사용하는 서비스 제공자 인터페이스 
// (제공자 등록 API, provider registration API) 
public interface ServiceProvider {
    Service newService();
}

// 서비스 관리자 (서비스 접근 API)
public class ServiceManager {
    private static final Map<String, ServiceProvider> providers = new HashMap<>();
    public static final String DEFAULT_PROVIDER_NAME = "<def>";

    // 서비스 제공자 등록 메서드
    public static void registerDefaultProvider(ServiceProvider p) {
        registerProvider(DEFAULT_PROVIDER_NAME, p);
    }

    public static void registerProvider(String name, ServiceProvider p) {
        providers.put(name, p);
    }

    // 서비스 인스턴스를 반환하는 정적 팩토리 메서드
    public static Service getService(String name) {
        ServiceProvider p = providers.get(name);
        if (p == null) {
            throw new IllegalArgumentException(
                    "No provider registered with name: " + name);
        }
        return p.newService();
    }
}


// 첫 번째 서비스 구현체
public class BasicService implements Service {
 
    @Override
    public void execute() {
        System.out.println("Executing BasicService");
    }

    public static class BasicServiceProvider implements ServiceProvider {
        @Override
        public Service newService() {
            return new BasicService();
        }
    }
}

// 두 번째 서비스 구현체
public class AdvancedService implements Service {
    @Override
    public void execute() {
        System.out.println("Executing AdvancedService");
    }

    public static class AdvancedServiceProvider implements ServiceProvider {
        @Override
        public Service newService() {
            return new AdvancedService();
        }
    }
}

// 실행
public class Main {
    public static void main(String[] args) {
        // 서비스 제공자 등록
        ServiceManager.registerProvider("basic", new BasicService.BasicServiceProvider());
        ServiceManager.registerProvider("advanced", new AdvancedService.AdvancedServiceProvider());

        // 서비스 인스턴스를 가져와 실행
        Service basicService = ServiceManager.getService("basic");
        basicService.execute();

        Service advancedService = ServiceManager.getService("advanced");
        advancedService.execute();
    }
}

 

 

"반환할 객체의 클래스가 존재하지 않아도 된다" 이 표현의 본질적인 의미는 정적 팩토리 메서드가 더 높은 수준의 추상화와 유연성을 제공하는 것을 의미합니다. 즉, 정적 팩토리 메서드를 정의할 때 구체적인 클래스를 명시적으로 참조하지 않아도 된다라는 의미입니다.

 

서비스 관리자 ( ServiceManager.java ) 소스를 보시면 서비스 인터페이스는 타입으로 하는 정적 팩토리 메서드가 구현되어 있습니다. 이때 구체적은 인스턴스를 참조하지 않습니다. 이를 '늦은 클래스 바인딩(late class binding)' 이라고 합니다. 정정 팩토리 메서드가 구현될 때는 실제로 반환될 객체의 구체적인 클래스가 아직 정의되지 않아도 됩니다.

 

예를들어, Service 인터페이스 구현체는  ServiceManager 소스가 작성되는 시점에 존재하지 않아도 됩니다. 나중에 다양한 Service 구현체들이 개발되고 ServiceManager 에 등록되면, 이러한 구현체들은 ServiceManager의 정적 팩토리 메서드를 통해 생성되고 반환될 수 있습니다. 이 과정에서 ServiceManager 소스는 변경되지 않고 확장성을 열려 있게 됩니다.

 

간단히 말해서 코드가 더 유연하고 확장 가능하도록 설계될 수 있음을 의미합니다.

 

 

단점

 

1. 상속을 하려면 public or protected 생성자가 필요하니 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다

 

정적 팩토리 메서드만 제공하고 생성자를 private 또는 package-private(protected)로 제한하는 클래스는 상속을 할 수 없고, 특정 상황에서 제약이 될 수 있스니다.

 

상속을 위해 하위 클래스가 상위 클래스의 생성자를 호출할 수 있어야 하는데, 하지만 클래스의 모든 생성자가 private or protected인 경우 다른 패키지에 있는 클래스는 이를 상속 받을 수 없습니다. 

정적 팩토리 메서드만으로 제공하는 인스턴스는 이러한 제약을 가지며, 이는 클래스의 인스턴스 생성을 완전히 제어하려는 의도 입니다.

 

public class UtilityClass {
    // private 생성자로 인스턴스화 방지
    private UtilityClass() {
        throw new AssertionError();
    }

    // 정적 팩토리 메서드만으로 인스턴스를 제공
    public static UtilityClass newInstance() {
        return new UtilityClass();
    }

    public void utilityMethod() {
        System.out.println("Utility method executed.");
    }
}


public class ExtendedUtilityClass extends UtilityClass {
    // 컴파일 에러: UtilityClass에는 접근할 수 있는 생성자가 없음
}

 

 

 

위 코드에 부가적인 설명으로 부모 클래스를 상속을 받게되면 super(); 키워드 호출로 부모 생성자를 호출할 수 있어야 합니다. 그럼 부모와 자식 클래스는 같은 참조값을 가진 인스턴스로 heap 메모리에 올라가게 되는데요. private로 막혀 버리면 사용할 수 없습니다.

 

정적 팩토리 메서드만을 제공하는 클래스 설계는 이 클래스가 상속되지 않도록 의도적으로 제한하는 경우가 많습니다. 클래스의 인스턴스화를 엄격히 제한합니다. 예를 들어, 유틸리티 클래스나 싱글톤 구현에 주로 사용합니다.

 

 

2. 정적 팩토리 메서드는 프로그래머가 찾기 어렵다

해당 단점은 자바 API 문서에서 생성자와는 달리 정적 팩터리 메서드는 명확하게 강조 되지 않습니다. 또한, 코드 내에서 직관적으로 눈에 띄지 않습니다.

 

  • API 문서 : 자바의 API 문서에서는 생성자가 클래스 설명의 시작부에 명시적으로 나열 됩니다. 반면, 정적 팩터리 메서드는 일반 메서드로 분류되어 생성자 만큼 두드러지게 보이지 않습니다. 이와 같은 문제로 인해 API 문서에서 정적 팩터리 메서드를 간과하기 쉽습니다.
  • 명명 규칙 : 생성자는 클래스 이름을 사용하기 때문에 명확하고 일관된 명명 방식을 갖습니다. 하지만 정적 팩터리 메서드는 개발자가 임의로 이름을 지정할 수 있기 때문에, 메서드의 목적이나 기능을 명확히 드러내는 이름을 사용하지 않는 경우 사용자가 해당 메서드의 기능을 쉽게 파악하기 어려울 수 있습니다. 이러한 문제를 해결하기 위해 명확한 명명 규칙을 가지고 있습니다.

 

명명 규칙

from : 매개변수를 하나 받아서 해당 타입의 인스턴스를 반환하는 형변환 메서드

 

public class FromExample {
    public static void main(String[] args) {
        // LocalDateTime 인스턴스를 사용
        LocalDateTime localDateTime = LocalDateTime.now();

        // LocalDateTime에서 LocalDate 인스턴스 생성
        LocalDate date = LocalDate.from(localDateTime);

        System.out.println("LocalDateTime: " + localDateTime);
        System.out.println("Converted LocalDate: " + date);
    }
}

 

 

위 코드에서 LocalDate.from 메서드는 LocalDateTime  객체로 부터 LocalDate 인스턴스를 생성 합니다.  

쉽게 뒤에서 부터 읽으면 localdatatime type 으로 부터 localdate 인스턴스를 생성한다.

 

// StringConvertible 인터페이스
public interface StringConvertible {
    String convertToString();
}

// MyData 클래스
public class MyData implements StringConvertible {
    private String data;

    private MyData(String data) {
        this.data = data;
    }

    // from 정적 팩토리 메서드
    public static StringConvertible from(String data) {
        return new MyData(data);
    }

    @Override
    public String convertToString() {
        return "MyData: " + data;
    }

    // main 메서드
    public static void main(String[] args) {
        // from 메서드를 사용하여 MyData 인스턴스 생성
        StringConvertible myData = MyData.from("Hello, World!");
        System.out.println(myData.convertToString());
    }
}

 

 

of : 여러개의 매개변수를 받아 적합한 타입의 인스턴스를 반환하는데 사용합니다. 해당 메서드는 주로 간견하고 명확한 인스턴스 생성을 위해 사용됩니다.

 

public class AggregateExample {
    public static void main(String[] args) {
        // of 메서드를 사용하여 불변 Set 인스턴스 생성
        Set<String> fruits = Set.of("Apple", "Banana", "Cherry");
        System.out.println(fruits);
    }
}

public class AggregateExample {
    public static void main(String[] args) {
        // of 메서드를 사용하여 불변 List 인스턴스 생성
        List<Integer> numbers = List.of(1, 2, 3, 4, 5);
        System.out.println(numbers);
    }
}

// 불변 인스턴스란?
// List 인터페이스를 구현하는 대부분의 클래스는 변경 가능한 mutable 컬렉션입니다.
// 자바9 이상에서 List.of() 와 같은 정적 팩토리 메서드는 불변 리스트로 생성됩니다.
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
numbers.add(3); // UnsupportedOperationException 발생

 

 

public class Point {
    private int x;
    private int y;

    // private 생성자
    private Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    // of 정적 팩토리 메서드
    public static Point of(int x, int y) {
        return new Point(x, y);
    }

    @Override
    public String toString() {
        return "Point{" +
               "x=" + x +
               ", y=" + y +
               '}';
    }

    // main 메서드
    public static void main(String[] args) {
        // of 메서드를 사용하여 Point 인스턴스 생성
        Point point = Point.of(5, 10);
        System.out.println(point);
    }
}

 

간과하기 쉬운점이 하나 있는데요. Point.of 같이 객체를 생성하는 방법도 유용합니다. 이펙티브 자바에서는 집계 메서드에 of 를 설명하였지만 제가 생각하기로는 메서드가 어떻게 사용되었느냐가 아니라 of 라는 메서드 이름이 어떤 역할을 하는지 명확하게 전달해준다는 것 입니다. 따라서 of 메서드를 사용할 때는 이런한 관점을 생각하고 사용하는게 좋습니다.

 

 

valueOf : 주어진 매개변수를 기반으로 해당 타입의 인스턴스를 반환합니다. 이 메서드는 주로 기본 타입의 값을 래퍼 클래스 타입으로 변환 하거나, 문자열로 부터 복잡한 객체를 생성할 때 사용합니다.

 

public class ValueOfExample {
    public static void main(String[] args) {
        // 문자열을 사용하여 Integer 객체 생성
        Integer intValueFromString = Integer.valueOf("123");
        System.out.println("Integer from String: " + intValueFromString);
    }
}

 

valueOf 메서드는 기본 타입이나 문자열 등의 값을 받아 해당 타입의 객체를 생성하는데 자주 사용됩니다. 

 

 

instance or getInstance : 해당 명명 규칙은 특정 타입의 지정된 인스턴스를 반환하며, 주로 싱글톤 패턴이나 인스턴스 캐싱 또는 조건에 따른 인스턴스 반환등에 사용됩니다. 특히 조건에 따른 인스턴스 반환은 (매개변수를 입력 받아 사용) 매개변수로 명시한 인스턴스를 반환하지만 같은 인스턴스임을 보장하지는 않습니다. 아래 예시에서 보면 매번 다른 인스턴스가 반환됩니다.

 

// 싱글톤 사용 예시
public class Singleton {
    // 유일한 인스턴스를 저장하는 private static 변수
    private static final Singleton instance;

    // private 생성자로 외부에서의 인스턴스화 방지
    private Singleton() {}

    // 인스턴스를 반환하는 정적 팩토리 메서드
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

public class SingletonInstance {
	
    // 미리 클래스 영역에 로드 하고 불변으로 사용
    private static final SingletonInstance INSTANCE = new SingletonInstance();
    
    private SingletonInstance() {
    }
    
    public static SingletonInstance getInstance() {
        return INSTANCE;
    }
}

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

// 인스턴스 캐싱 사용 예시
public class ComplexObjectFactory {
    private static Map<String, ComplexObject> cache = new HashMap<>();

    public static ComplexObject getInstance(String key) {
        if (!cache.containsKey(key)) {
            // 복잡한 생성 과정
            ComplexObject newObject = new ComplexObject(key);
            cache.put(key, newObject);
        }
        return cache.get(key);
    }
}

class ComplexObject {
    private String key;

    public ComplexObject(String key) {
        this.key = key;
        // 여기에 복잡한 초기화 로직이 포함될 수 있습니다.
    }

    // 여기에 다른 메서드들이 있을 수 있습니다.
}


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


// 조건에 따른 인스턴스 반환 예시
public interface PaymentProcessor {
    void processPayment(double amount);
}

public class CreditCardProcessor implements PaymentProcessor {
    @Override
    public void processPayment(double amount) {
        System.out.println("Processing credit card payment of $" + amount);
    }
}

public class PaypalProcessor implements PaymentProcessor {
    @Override
    public void processPayment(double amount) {
        System.out.println("Processing PayPal payment of $" + amount);
    }
}

public class PaymentProcessorFactory {
    public static PaymentProcessor getInstance(String type) {
        switch (type) {
            case "creditcard":
                return new CreditCardProcessor();
            case "paypal":
                return new PaypalProcessor();
            default:
                throw new IllegalArgumentException("Unknown payment type: " + type);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        PaymentProcessor processor = PaymentProcessorFactory.getInstance("creditcard");
        processor.processPayment(100.0);
    }
}

 

 

newInstance or create : instance or getInstance는 동일한 인스턴스 반환을 보장하지 않지만, newInstance와 create 는 매번 새로운 인스턴스를 반활할 때 사용합니다. 생성자와 유사하지만 더 유연하고 명확한 이름을 제공 합니다.

 

public class User {
    private String name;
    private int age;

    private User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static User create(String name, int age) {
        return new User(name, age);
    }

    // User 클래스의 다른 메서드들...
}

public class Main {
    public static void main(String[] args) {
        User user = User.create("Alice", 30);
        // user 객체 사용
    }
}

 

 

getType : getInstance와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 쓴다. "Type" 은 팩터리 메서드가 반환할 객체의 타입니다.

쉽게 말해 특정 타입의 객체를 반환할 때 사용하거나, 특정 상황에 따라 다양한 구현체 중 하나를 반환할 때 사용합니다.

 

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.FileStore;

public class FileStoreExample {
    public static void main(String[] args) {
        try {
            Path path = Paths.get("/some/directory/file.txt");
            FileStore fileStore = Files.getFileStore(path);
            System.out.println("File store: " + fileStore);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

위 코드에서 Files.getFileStore 메서드는 Path 객체를 매개변수로 입력 받아 해당 파일이 위치한 FileStore 객체를 반환 합니다. 

위 경우 FileStore 타입의 객체를 반환하므로, getFileStore 라는 메서드 이름은 반환되는 객체의 타입을 나타내고 있습니다.

 

 

* 정적 팩터리 메서드와 public 생성자는 각자의 쓰임새가 있으니 상대적인 장단점을 이해하고 사용하는 것이 좋다. 그렇다고 하더라도 정적 팩터리를 사용하는게 유리한 경우가 더 많으므로 무작정 public 생성자를 제공하던 습관이 있다면 고치자

 

 

출처 : 이펙티브자바 Effective Java