리플렉션은 힙 영역에 로드된 Class 타입의 객체를 통해, 원하는 클래스의 인스턴스를 생성할 수 있도록 지원하고, 인스턴스의 필드와 메소드를 접근 제어자와 상관 없이 사용할 수 있도록 지원하는 API입니다.
JVM의 클래스 로더가 클래스 파일을 로딩하면, 해당 클래스의 정보를 담은 Class 타입의 객체를 생성하여 힙(Heap) 영역에 저장합니다. 리플렉션은 이 Class 객체를 통해 클래스의 구조를 분석하고 조작합니다.
⚠️ 주의: Class 객체는 new 키워드로 생성하는 일반 인스턴스와는 다릅니다.
장점 ✅
단점 ⚠️
Class - 클래스 정보Constructor - 생성자 정보Method - 메서드 정보Field - 필드 정보리플렉션을 사용하려면 먼저 Class 객체를 얻어야 합니다.
이미 생성된 인스턴스에서 Class 객체를 얻는 방법입니다.
public static void main(String[] args) {
String str = new String("Class 클래스 테스트");
*// getClass() 메서드로 얻기*
Class<? extends String> cls = str.getClass();
System.out.println(cls); *// class java.lang.String*
}
클래스 이름 뒤에 .class를 붙여 직접 얻는 방법입니다.
Class<?> clazz = String.class;
클래스의 전체 경로(FQCN)를 문자열로 전달하는 방법입니다.
Class<?> clazz = Class.forName("java.lang.String");
💡 Tip: Class.forName()은 동적으로 클래스를 로드할 때 유용하지만, ClassNotFoundException 예외 처리가 필요합니다.
Class 객체를 얻었다면, 클래스의 다양한 메타데이터를 조회할 수 있습니다.
Class<?> clazz = String.class;
// 클래스 이름 얻기
System.out.println("Class name: " + clazz.getName());
// 모든 메서드 얻기
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println("Method name: " + method.getName());
}
// 모든 필드 얻기
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println("Field name: " + field.getName());
}
리플렉션의 가장 강력한 기능 중 하나는 private 필드에도 접근할 수 있다는 것입니다.
getField() - public 필드에만 접근getDeclaredField() - 모든 접근 제어자의 필드에 접근class Example {
public int publicField;
private Stirng privateField;
}
// public 필드 접근
Field publicField = clazz.getField("publicField");
*// private 필드 접근*
Field privateField = clazz.getDeclaredField("privateField");
*// private 필드는 접근 권한을 변경해야 함*
privateField.setAccessible(true);
get()과 set() 메서드를 사용합니다.
Example example = new Example();
*// public 필드 읽기*
int publicFieldValue = (Integer) publicField.get(example);
System.out.println("Public Field Value: " + publicFieldValue);
*// private 필드 읽기*
String privateFieldValue = (String) privateField.get(example);
System.out.println("Private Field Value: " + privateFieldValue);
*// 필드 값 수정*
publicField.set(example, 10); *// public 필드 수정*
privateField.set(example, "Hello World"); *// private 필드 수정*
리플렉션을 사용하면 런타임에 동적으로 메서드를 호출할 수 있습니다.
class Example {
public void publicMethod() {
System.out.println("Public method");
}
private void privateMethod() {
System.out.println("Private method");
}
}
Class<?> clazz = Example.class;
*// public 메서드 접근*
Method publicMethod = clazz.getMethod("publicMethod");
*// private 메서드 접근*
Method privateMethod = clazz.getDeclaredMethod("privateMethod");
*// private 메서드는 접근 가능하도록 설정*
privateMethod.setAccessible(true);
invoke() 메서드를 사용하여 메서드를 실행합니다.
Example example = new Example();
// public 메서드 실행
publicMethod.invoke(example);
// private 메서드 실행
privateMethod.invoke(example);
💡 invoke() 메서드란?
리플렉션 API의 Method 클래스가 제공하는 메서드로, 런타임에 특정 객체의 메서드를 동적으로 실행할 수 있게 해줍니다.
리플렉션을 사용하면 private 생성자로도 객체를 생성할 수 있습니다.
class Example {
public Example() {
System.out.println("Public constructor");
}
private Example(String arg) {
System.out.println("Private constructor: " + arg);
}
}
Class<?> clazz = Example.class;
*// public 생성자 접근*
Constructor<?> publicConstructor = clazz.getConstructor();
*// private 생성자 접근*
Constructor<?> privateConstructor = clazz.getDeclaredConstructor(String.class);
*// private 생성자는 접근 가능하도록 설정*
privateConstructor.setAccessible(true);
// public 생성자를 사용한 객체 생성
Example example1 = (Example) publicConstructor.newInstance();
*// private 생성자를 사용한 객체 생성*
Example example2 = (Example) privateConstructor.newInstance("argument");
이번 포스팅에서는 자바 리플렉션의 기본 개념과 사용 방법을 알아보았습니다.
핵심 내용 요약
getClass(), .class, Class.forName()setAccessible(true)로 private 멤버에 접근 가능다음 포스팅에서는 리플렉션의 실전 활용과 심화 내용을 다룰 예정입니다.
예정된 주제