Post

자바_리플랙션_02


생성자

  • 자바의 생성자는 대문자 C -> 제네릭의 Constructor의 인스턴스로 나타냄.
  • 객체에는 클래스 생성자의 모든 정보를 포함하고 있음.
    • 매개변수의 개수
    • 매개변수의 타입
  • 생성자는 여러개를 가질 수 있음.



생성자를 가져오는 방법

  • Class.getDeclaredConstrors()
    • 모든 클래스의 생성자를 반환한다.
    • 접근제한자 상관없이 전부 반환
  • Class.getConstructors()
    • 접근제한자 public 생성자만 반환
  • 특정 생성자를 찾을 때 매개변수 타입을 알고있다면?
    • 위 두 개의 클래스에 매개변수 타입을 적으면 된다.
1
2
3
4
5
6
7
8
9
10
11
    public static void printCon(Class<?> clazz) {
      Constructor<?>[] constructor = clazz.getDeclaredConstrors();

      for(int i = 0; i < constructor.length; i++) {
        Class<?>[] parameterTypes = constructor[i].getParameterTypes();
        
        List<String> parameterList = Array.stream(parameterTypes)
        .map(type -> type.getSimpleName())
        .collect(Collectors.toList());
      }
    }



객체를 생성하는 효과적인 방법

  • 객체를 생성하는 단일 팩토리 메서드를 실행하고 메서드에 전달한 인수에 따라 주어진 클래스에 알맞는 생성자를 찾아서 객체를 생성하는 동안 해당 생성자를 찾는것



객체 생성

  • Constructor.newInstance(Object …)
    • 생성자에 선언된 순서대로 생성자 매개변수에 응답하는 가변인수를 받는 메서드
  • 인수가 올바른 타입과 순서대로 전달
    • 생성자에 접근할 수 있다면 특정 클래스 생성자가 호출
    • 성공하면 클래스의 객체가 반환
  • 실패한다면?
    • 예외를 던짐
1
2
3
4
5
6
7
8
9
public static <T> T createInstanceWithArg(Class<T> clazz, Object ...arg) {
  for(Constructor<?> constructor : clazz.getDeclaredConstrors()) {
    if(constructor.getParameterTypes().length == args.length) {
      return (T)constructor.newInstance(args);  
    }
  }

  return null;
}
  • 객체를 반환하고, 첫 인수로 인스턴스화하는 클래스와 클래스 생성자에 전달하려고 하는 가변인수의 목록을 받음.
  • 조건문에는 생성자의 인수 개수가 메서드에 전달하고자 하는 인수의 개수와 일치하는 생성자를 찾음.
  • 찾으면 newInstance()를 호출해 메서드에 사용한 인수를 전달.
  • 만약 생성자를 못찾으면 null을 받음


  • 리플렉션 없이 팩토리 메서드를 실행하는건 사실상 불가능하다.





제한된 생성자 접근

  • Private으로 되어있는 생성자는 외부에서 접근할 수 없기에 new 로 생성할 수 없다.

  • 리플렉션을 이용하면 아래 4개의 접근 제한자여도 접근이 가능하다.
    1. public
    2. protected
    3. package-private
    4. and private
  • newInstance() 으로 제한된 생성자 중 하나를 이용해 클래스를 생성할 수 있음.
  • 예외상황 -> 접근하려는 클래스가 속한 모듈이 특정 클래스에 접근하지 못하는 경우
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ServerConfiguration {
    private static ServerConfiguration serverConfigurationInstance;

    private final InetSocketAddress serverAddress;
    private final String greetingMessage;

    private ServerConfiguration(int port, String greetingMessage) {
        this.greetingMessage = greetingMessage;
        this.serverAddress = new InetSocketAddress("localhost", port);
        if (serverConfigurationInstance == null) {
            serverConfigurationInstance = this;
        }
    }

    public static ServerConfiguration getInstance() { return serverConfigurationInstance; }

    public InetSocketAddress getServerAddress() { return this.serverAddress; }

    public String getGreetingMessage() { return this.greetingMessage; }
}
1
2
3
4
5
6
7
public static void initConfiguration() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor<ServerConfiguration> constructor =
                ServerConfiguration.class.getDeclaredConstructor(int.class, String.class);

        constructor.setAccessible(true);  // 핵심
        constructor.newInstance(8080, "Good Day!");
    }
  • newInstance() 이전에 setAccessible() 메서드를 true로 두면 접근이 가능하다.


주의사항

  • 특별한 경우가 아니면 사용하면 안됨.
  • 내부 생성자나 소유하지않은 클래스에서 의도적으로 제한된 생성자에는 남용X.
  • 합당한 경우가 아니라면 절대 사용X




생성자 클래스로 package-private 인스턴스화

  • public 클래스는 어디서는 접근이 가능함.
  • 하지만 접근제한자가 없다면 해당 패키지 클래스에서만 접근 가능.
  • package-private 클래스도 패키지 외부에서 꼭 접근해야하는 경우가 있음.
    • 읽기
    • 초기화
    • 외부 라이브러리 사용
1
2
3
4
5
public Object createClassInstance(Constructor<?> packagePrivateClassCtor, Object... ctorArgs) {
  packagePrivateClassCtor.setAccessible(true);

  return packagePrivateClassCtor.newInstance(ctorArgs);
}
  • package-private 클래스 객체를 만들기 위해서는 newInstance 호출전에 setAccessible() 를 true로 설정하는데, public 생성자 외의 생성자를 호출할 떄와 같음.


필요한 사례

  1. 특정 상품의 최저가를 찾을 떄 외부 서비스와 소통(모든 클래스가 담긴 자바 패키지)

  2. 의존성 주입

    • 외부패키지에서 package-private를 인스턴스화 하는 경우
    • 애플리케이션 시작시 클래스를 자동 생성하는 경우
    • 의존성주입 클래스는 제한된 package-private 클래스를 모두 생성하고 연결하기 위해 접근 권한이 있어야함.
This post is licensed under CC BY 4.0 by the author.