런타임에 클래스 정보 조작하기 - Reflection API의 기초
Java Reflection
public class WhyReflection {
static class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
private void secretMethod() {
System.out.println("비밀 메서드!");
}
}
public static void main(String[] args) throws Exception {
System.out.println("=== Reflection이란? ===\n");
System.out.println("📌 정의");
System.out.println("런타임에 클래스 정보를 조사하고 조작하는 API\n");
System.out.println("📌 가능한 것들");
System.out.println("✅ 클래스 정보 조회");
System.out.println("✅ private 필드 접근");
System.out.println("✅ private 메서드 호출");
System.out.println("✅ 동적 인스턴스 생성");
System.out.println("✅ 애너테이션 읽기\n");
// Reflection 예제
User user = new User("Alice", 25);
Class<?> clazz = user.getClass();
// private 필드 접근
java.lang.reflect.Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
System.out.println("이름: " + nameField.get(user));
// private 메서드 호출
java.lang.reflect.Method secretMethod = clazz.getDeclaredMethod("secretMethod");
secretMethod.setAccessible(true);
secretMethod.invoke(user);
System.out.println("\n📌 사용 사례");
System.out.println("- 프레임워크 (Spring, Hibernate)");
System.out.println("- 테스트 라이브러리");
System.out.println("- DI 컨테이너");
System.out.println("- ORM 매핑");
System.out.println("- 직렬화/역직렬화\n");
System.out.println("⚠️ 주의사항");
System.out.println("- 성능 오버헤드");
System.out.println("- 타입 안전성 상실");
System.out.println("- 보안 위험");
System.out.println("- 캡슐화 파괴");
}
}public class ReflectionAPI {
public static void main(String[] args) {
System.out.println("=== Reflection API ===\n");
System.out.println("📦 주요 클래스");
System.out.println("java.lang.Class");
System.out.println(" - 클래스/인터페이스 표현\n");
System.out.println("java.lang.reflect.Field");
System.out.println(" - 필드 정보\n");
System.out.println("java.lang.reflect.Method");
System.out.println(" - 메서드 정보\n");
System.out.println("java.lang.reflect.Constructor");
System.out.println(" - 생성자 정보\n");
System.out.println("java.lang.reflect.Modifier");
System.out.println(" - 접근 제어자 정보\n");
System.out.println("java.lang.reflect.Array");
System.out.println(" - 배열 조작\n");
}
}public class GetClassObject {
static class Person {
String name;
}
public static void main(String[] args) throws ClassNotFoundException {
System.out.println("=== Class 객체 얻기 ===\n");
// 1. .class 리터럴
Class<Person> clazz1 = Person.class;
System.out.println("1. .class: " + clazz1.getName());
// 2. getClass()
Person person = new Person();
Class<?> clazz2 = person.getClass();
System.out.println("2. getClass(): " + clazz2.getName());
// 3. Class.forName()
Class<?> clazz3 = Class.forName("GetClassObject$Person");
System.out.println("3. forName(): " + clazz3.getName());
// 같은 객체
System.out.println("\n같은 객체? " + (clazz1 == clazz2));
System.out.println("같은 객체? " + (clazz2 == clazz3));
// 기본 타입
System.out.println("\n=== 기본 타입 ===");
Class<?> intClass = int.class;
Class<?> stringClass = String.class;
System.out.println("int: " + intClass);
System.out.println("String: " + stringClass);
}
}import java.lang.reflect.*;
public class ClassInformation {
static class User {
private String name;
public int age;
public User() {}
public User(String name) { this.name = name; }
public void publicMethod() {}
private void privateMethod() {}
}
public static void main(String[] args) {
System.out.println("=== Class 정보 ===\n");
Class<User> clazz = User.class;
// 기본 정보
System.out.println("클래스명: " + clazz.getName());
System.out.println("단순명: " + clazz.getSimpleName());
System.out.println("패키지: " + clazz.getPackage());
System.out.println("모디파이어: " + Modifier.toString(clazz.getModifiers()));
// 필드 수
System.out.println("\n=== 필드 ===");
Field[] fields = clazz.getDeclaredFields();
System.out.println("필드 수: " + fields.length);
for (Field field : fields) {
System.out.println(" " + field.getName() + ": " + field.getType());
}
// 메서드 수
System.out.println("\n=== 메서드 ===");
Method[] methods = clazz.getDeclaredMethods();
System.out.println("메서드 수: " + methods.length);
for (Method method : methods) {
System.out.println(" " + method.getName());
}
// 생성자
System.out.println("\n=== 생성자 ===");
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
System.out.println("생성자 수: " + constructors.length);
for (Constructor<?> constructor : constructors) {
System.out.println(" " + constructor);
}
}
}import java.lang.reflect.*;
public class FieldAccess {
static class Person {
public String publicField = "public";
private String privateField = "private";
protected String protectedField = "protected";
String defaultField = "default";
}
public static void main(String[] args) throws Exception {
System.out.println("=== 필드 접근 ===\n");
Person person = new Person();
Class<?> clazz = Person.class;
// public 필드 - 직접 접근 가능
Field publicField = clazz.getField("publicField");
System.out.println("public: " + publicField.get(person));
// private 필드 - setAccessible 필요
System.out.println("\n=== private 필드 ===");
Field privateField = clazz.getDeclaredField("privateField");
privateField.setAccessible(true); // 접근 허용
System.out.println("private: " + privateField.get(person));
// 값 변경
privateField.set(person, "modified");
System.out.println("변경 후: " + privateField.get(person));
// 모든 필드
System.out.println("\n=== 모든 필드 ===");
Field[] allFields = clazz.getDeclaredFields();
for (Field field : allFields) {
field.setAccessible(true);
System.out.printf("%s = %s%n", field.getName(), field.get(person));
}
}
}import java.lang.reflect.*;
public class FieldInformation {
static class Sample {
public static final String CONSTANT = "CONST";
private int privateInt;
public String publicString;
protected double protectedDouble;
transient int transientInt;
volatile boolean volatileBool;
}
public static void main(String[] args) throws Exception {
System.out.println("=== 필드 정보 ===\n");
Class<?> clazz = Sample.class;
for (Field field : clazz.getDeclaredFields()) {
System.out.println("필드: " + field.getName());
System.out.println(" 타입: " + field.getType());
System.out.println(" 모디파이어: " + Modifier.toString(field.getModifiers()));
// 속성 체크
if (Modifier.isStatic(field.getModifiers())) {
System.out.println(" → static");
}
if (Modifier.isFinal(field.getModifiers())) {
System.out.println(" → final");
}
if (Modifier.isTransient(field.getModifiers())) {
System.out.println(" → transient");
}
if (Modifier.isVolatile(field.getModifiers())) {
System.out.println(" → volatile");
}
System.out.println();
}
}
}import java.lang.reflect.*;
public class MethodInvocation {
static class Calculator {
public int add(int a, int b) {
return a + b;
}
private int multiply(int a, int b) {
return a * b;
}
public static String greet(String name) {
return "Hello, " + name;
}
}
public static void main(String[] args) throws Exception {
System.out.println("=== 메서드 호출 ===\n");
Calculator calc = new Calculator();
Class<?> clazz = Calculator.class;
// public 메서드 호출
Method addMethod = clazz.getMethod("add", int.class, int.class);
Object result = addMethod.invoke(calc, 10, 5);
System.out.println("add(10, 5) = " + result);
// private 메서드 호출
System.out.println("\n=== private 메서드 ===");
Method multiplyMethod = clazz.getDeclaredMethod("multiply", int.class, int.class);
multiplyMethod.setAccessible(true);
Object result2 = multiplyMethod.invoke(calc, 10, 5);
System.out.println("multiply(10, 5) = " + result2);
// static 메서드 호출
System.out.println("\n=== static 메서드 ===");
Method greetMethod = clazz.getMethod("greet", String.class);
Object result3 = greetMethod.invoke(null, "Alice"); // static은 null
System.out.println(result3);
}
}import java.lang.reflect.*;
public class MethodInformation {
static class Sample {
public void publicMethod() {}
private int privateMethod(String s) { return 0; }
protected static void protectedStaticMethod(int a, int b) {}
public final void finalMethod() {}
}
public static void main(String[] args) {
System.out.println("=== 메서드 정보 ===\n");
Class<?> clazz = Sample.class;
for (Method method : clazz.getDeclaredMethods()) {
System.out.println("메서드: " + method.getName());
System.out.println(" 반환 타입: " + method.getReturnType());
System.out.println(" 모디파이어: " + Modifier.toString(method.getModifiers()));
// 파라미터
Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length > 0) {
System.out.print(" 파라미터: ");
for (Class<?> paramType : paramTypes) {
System.out.print(paramType.getSimpleName() + " ");
}
System.out.println();
}
// 예외
Class<?>[] exceptionTypes = method.getExceptionTypes();
if (exceptionTypes.length > 0) {
System.out.print(" 예외: ");
for (Class<?> exType : exceptionTypes) {
System.out.print(exType.getSimpleName() + " ");
}
System.out.println();
}
System.out.println();
}
}
}import java.lang.reflect.*;
public class InstanceCreation {
static class Person {
private String name;
private int age;
public Person() {
this("Unknown", 0);
}
public Person(String name) {
this(name, 0);
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
public static void main(String[] args) throws Exception {
System.out.println("=== 인스턴스 생성 ===\n");
Class<Person> clazz = Person.class;
// 기본 생성자
Person p1 = clazz.getDeclaredConstructor().newInstance();
System.out.println("기본 생성자: " + p1);
// 파라미터 있는 생성자
Constructor<Person> constructor = clazz.getConstructor(String.class, int.class);
Person p2 = constructor.newInstance("Alice", 25);
System.out.println("파라미터 생성자: " + p2);
// Class.newInstance() (deprecated)
System.out.println("\n⚠️ Class.newInstance()는 deprecated");
System.out.println("대신 getDeclaredConstructor().newInstance() 사용");
}
}import java.lang.reflect.*;
public class ConstructorInformation {
static class Sample {
public Sample() {}
public Sample(int a) {}
private Sample(String s) {}
protected Sample(int a, String s) {}
}
public static void main(String[] args) {
System.out.println("=== 생성자 정보 ===\n");
Class<?> clazz = Sample.class;
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
System.out.println("생성자 수: " + constructors.length + "\n");
for (Constructor<?> constructor : constructors) {
System.out.println("생성자: " + constructor.getName());
System.out.println(" 모디파이어: " + Modifier.toString(constructor.getModifiers()));
Class<?>[] paramTypes = constructor.getParameterTypes();
if (paramTypes.length > 0) {
System.out.print(" 파라미터: ");
for (Class<?> paramType : paramTypes) {
System.out.print(paramType.getSimpleName() + " ");
}
System.out.println();
}
System.out.println();
}
}
}import java.lang.reflect.*;
interface Drawable {
void draw();
}
interface Movable {
void move();
}
class Shape {
String color;
}
class Circle extends Shape implements Drawable, Movable {
double radius;
@Override
public void draw() {}
@Override
public void move() {}
}
public class TypeInformation {
public static void main(String[] args) {
System.out.println("=== 타입 정보 ===\n");
Class<Circle> clazz = Circle.class;
// 슈퍼클래스
Class<?> superclass = clazz.getSuperclass();
System.out.println("슈퍼클래스: " + superclass.getName());
// 인터페이스
System.out.println("\n=== 인터페이스 ===");
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> intf : interfaces) {
System.out.println(" " + intf.getName());
}
// 타입 체크
System.out.println("\n=== 타입 체크 ===");
System.out.println("Drawable? " + Drawable.class.isAssignableFrom(clazz));
System.out.println("Shape? " + Shape.class.isAssignableFrom(clazz));
System.out.println("String? " + String.class.isAssignableFrom(clazz));
// 클래스 종류
System.out.println("\n=== 클래스 종류 ===");
System.out.println("인터페이스? " + clazz.isInterface());
System.out.println("배열? " + clazz.isArray());
System.out.println("Enum? " + clazz.isEnum());
System.out.println("프리미티브? " + clazz.isPrimitive());
}
}import java.lang.reflect.*;
import java.util.*;
public class GenericTypeInfo {
static class Container<T> {
private List<T> items;
public List<T> getItems() {
return items;
}
public void setItems(List<T> items) {
this.items = items;
}
}
public static void main(String[] args) throws Exception {
System.out.println("=== 제네릭 타입 ===\n");
Class<?> clazz = Container.class;
// 필드의 제네릭 타입
Field itemsField = clazz.getDeclaredField("items");
Type genericType = itemsField.getGenericType();
System.out.println("필드 타입: " + genericType);
if (genericType instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) genericType;
Type[] typeArgs = paramType.getActualTypeArguments();
System.out.println("타입 파라미터:");
for (Type typeArg : typeArgs) {
System.out.println(" " + typeArg);
}
}
// 메서드의 제네릭 타입
System.out.println("\n=== 메서드 타입 ===");
Method getItemsMethod = clazz.getMethod("getItems");
Type returnType = getItemsMethod.getGenericReturnType();
System.out.println("반환 타입: " + returnType);
}
}import java.lang.reflect.*;
public class ArrayHandling {
public static void main(String[] args) {
System.out.println("=== 배열 처리 ===\n");
// 배열 생성
int[] intArray = (int[]) Array.newInstance(int.class, 5);
System.out.println("배열 길이: " + Array.getLength(intArray));
// 값 설정
for (int i = 0; i < 5; i++) {
Array.setInt(intArray, i, i * 10);
}
// 값 읽기
System.out.println("\n배열 내용:");
for (int i = 0; i < Array.getLength(intArray); i++) {
System.out.println(" [" + i + "] = " + Array.getInt(intArray, i));
}
// 다차원 배열
System.out.println("\n=== 다차원 배열 ===");
int[][] matrix = (int[][]) Array.newInstance(int.class, 3, 3);
Array.setInt(Array.get(matrix, 0), 0, 1);
Array.setInt(Array.get(matrix, 1), 1, 2);
Array.setInt(Array.get(matrix, 2), 2, 3);
System.out.println("matrix[0][0] = " + Array.getInt(Array.get(matrix, 0), 0));
System.out.println("matrix[1][1] = " + Array.getInt(Array.get(matrix, 1), 1));
System.out.println("matrix[2][2] = " + Array.getInt(Array.get(matrix, 2), 2));
// 객체 배열
System.out.println("\n=== 객체 배열 ===");
String[] strArray = (String[]) Array.newInstance(String.class, 3);
Array.set(strArray, 0, "A");
Array.set(strArray, 1, "B");
Array.set(strArray, 2, "C");
for (int i = 0; i < Array.getLength(strArray); i++) {
System.out.println(" " + Array.get(strArray, i));
}
}
}import java.lang.reflect.*;
import java.util.*;
// 애너테이션
@interface Inject {}
class UserService {
@Inject
private UserRepository repository;
public void printInfo() {
System.out.println("UserService with " + repository);
}
}
class UserRepository {
public UserRepository() {
System.out.println("UserRepository 생성");
}
}
class SimpleContainer {
private Map<Class<?>, Object> instances = new HashMap<>();
public <T> T getInstance(Class<T> clazz) throws Exception {
// 이미 생성된 인스턴스 반환
if (instances.containsKey(clazz)) {
return clazz.cast(instances.get(clazz));
}
// 새 인스턴스 생성
T instance = clazz.getDeclaredConstructor().newInstance();
// @Inject 필드 주입
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Inject.class)) {
field.setAccessible(true);
Object dependency = getInstance(field.getType());
field.set(instance, dependency);
}
}
instances.put(clazz, instance);
return instance;
}
}
public class SimpleDIContainer {
public static void main(String[] args) throws Exception {
System.out.println("=== 간단한 DI 컨테이너 ===\n");
SimpleContainer container = new SimpleContainer();
UserService service = container.getInstance(UserService.class);
service.printInfo();
}
}import java.lang.reflect.*;
public class ObjectCopier {
static class Person {
private String name;
private int age;
private String email;
public Person(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", email='" + email + "'}";
}
}
public static <T> T copy(T source) throws Exception {
Class<?> clazz = source.getClass();
// 새 인스턴스 생성
@SuppressWarnings("unchecked")
T target = (T) clazz.getDeclaredConstructor().newInstance();
// 모든 필드 복사
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
Object value = field.get(source);
field.set(target, value);
}
return target;
}
public static void main(String[] args) throws Exception {
System.out.println("=== 객체 복사 ===\n");
Person original = new Person("Alice", 25, "alice@example.com");
System.out.println("원본: " + original);
Person copied = copy(original);
System.out.println("복사: " + copied);
System.out.println("\n다른 객체? " + (original != copied));
}
}// Reflection으로 toString() 생성
import java.lang.reflect.*;
public class Problem1 {
static class User {
private String name = "Alice";
private int age = 25;
private String email = "alice@example.com";
}
public static String toStringReflection(Object obj) throws Exception {
// 구현: 모든 필드를 "ClassName{field=value, ...}" 형식으로
return "";
}
public static void main(String[] args) throws Exception {
User user = new User();
System.out.println(toStringReflection(user));
// User{name=Alice, age=25, email=alice@example.com}
}
}정답:
정답 보기
public static String toStringReflection(Object obj) throws Exception {
Class<?> clazz = obj.getClass();
StringBuilder sb = new StringBuilder(clazz.getSimpleName()).append("{");
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
field.setAccessible(true);
sb.append(field.getName()).append("=").append(field.get(obj));
if (i < fields.length - 1) {
sb.append(", ");
}
}
return sb.append("}").toString();
}// 모든 getter 메서드 찾아서 호출
import java.lang.reflect.*;
public class Problem2 {
static class Person {
private String name = "Bob";
private int age = 30;
public String getName() { return name; }
public int getAge() { return age; }
public void setName(String name) { this.name = name; }
}
public static void callAllGetters(Object obj) throws Exception {
// 구현: get으로 시작하는 메서드만 호출하고 출력
}
public static void main(String[] args) throws Exception {
Person person = new Person();
callAllGetters(person);
// getName() = Bob
// getAge() = 30
}
}정답:
정답 보기
public static void callAllGetters(Object obj) throws Exception {
Class<?> clazz = obj.getClass();
for (Method method : clazz.getDeclaredMethods()) {
if (method.getName().startsWith("get") &&
method.getParameterCount() == 0) {
Object result = method.invoke(obj);
System.out.println(method.getName() + "() = " + result);
}
}
}// 모든 필드를 기본값으로 초기화
import java.lang.reflect.*;
public class Problem3 {
static class Data {
private String name = "Test";
private int count = 100;
private boolean flag = true;
}
public static void resetFields(Object obj) throws Exception {
// 구현: String→null, int→0, boolean→false
}
public static void main(String[] args) throws Exception {
Data data = new Data();
System.out.println("초기화 전: " + data.name + ", " + data.count + ", " + data.flag);
resetFields(data);
System.out.println("초기화 후: " + data.name + ", " + data.count + ", " + data.flag);
// null, 0, false
}
}정답:
정답 보기
public static void resetFields(Object obj) throws Exception {
Class<?> clazz = obj.getClass();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
Class<?> type = field.getType();
if (type == int.class) {
field.setInt(obj, 0);
} else if (type == boolean.class) {
field.setBoolean(obj, false);
} else if (!type.isPrimitive()) {
field.set(obj, null);
}
}
}Class<?> clazz = Object.class; // .class
Class<?> clazz = obj.getClass(); // getClass()
Class<?> clazz = Class.forName("Name"); // forName()Field field = clazz.getDeclaredField("name");
field.setAccessible(true); // private 접근
Object value = field.get(obj); // 읽기
field.set(obj, "new value"); // 쓰기Method method = clazz.getMethod("methodName", ParamType.class);
Object result = method.invoke(obj, args); // 호출Constructor<?> ctor = clazz.getDeclaredConstructor(ParamType.class);
Object obj = ctor.newInstance(args);⚠️ 성능: Reflection은 느림
⚠️ 보안: 캡슐화 파괴
⚠️ 타입: 컴파일 타임 체크 없음
✅ 프레임워크에서만 사용 권장