자바_리플랙션_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개의 접근 제한자여도 접근이 가능하다.
- public
- protected
- package-private
- 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 생성자 외의 생성자를 호출할 떄와 같음.
필요한 사례
특정 상품의 최저가를 찾을 떄 외부 서비스와 소통(모든 클래스가 담긴 자바 패키지)
의존성 주입
- 외부패키지에서 package-private를 인스턴스화 하는 경우
- 애플리케이션 시작시 클래스를 자동 생성하는 경우
- 의존성주입 클래스는 제한된 package-private 클래스를 모두 생성하고 연결하기 위해 접근 권한이 있어야함.
This post is licensed under CC BY 4.0 by the author.