🎇 Java
Java를 사용하다 보면 리플렉션(Reflection)이라는 용어를 자주 접한다.
대충 무엇인지는 알겠는데 설명해 주세요! 하면 못 할 것 같아서 정리해 본다😋😋
🪞 리플렉션, Reflection
리플렉션이란? 단어의 의미를 떠올려서, 거울에 반사된 어떤 대상을 제약 없이 사용하는 기술이다.
자세하게 설명하면, 힙 영역에 로드된 Class 타입의 객체를 통해서 인스턴스를 생성하고,
접근 지정자의 제약 없이 인스턴스의 메소드와 필드를 사용할 수 있게 하는 Java API이다.
😮 힙 영역에서 Class 타입 가져오기
힙 영역에 로드된 Class 타입은 다음 3가지 방법으로 가져올 수 있다.
- 클래스.class
- 인스턴스.getClass()
- Class.forName(클래스명)
public class GetClassTest {
public static void main(String[] args) throws ClassNotFoundException {
// 클래스.class로 가져오기
Class<Pokemon> pokemonClass = Pokemon.class;
// 인스턴스.getClass()로 가져오기
Pokemon pokemon = new Pokemon();
Class<? extends Pokemon> pokemonClass2 = pokemon.getClass();
// Class.forName(클래스명)로 가져오기
Class<?> pokemonClass3 = Class.forName("Reflection.Pokemon");
System.out.println(System.identityHashCode(pokemonClass));
System.out.println(System.identityHashCode(pokemonClass2));
System.out.println(System.identityHashCode(pokemonClass3));
}
}
😋 리플렉션 사용하기
리플렉션을 사용하기 앞서 사용할 객체는 다음과 같다. 포켓몬 짱..!
public class Pokemon {
public String name;
protected int stat;
private String hiddenPassive;
public Pokemon() {}
public Pokemon(String name, int stat, String hiddenPassive) {
this.name = name;
this.stat = stat;
this.hiddenPassive = hiddenPassive;
}
public void printName(String name) {
System.out.println("이 포켓몬의 이름은 " + name + "입니다.");
}
private void printHidden() {
System.out.println("히든 패시브는 " + this.hiddenPassive + "입니다.");
}
@Override
public String toString() {
return "\n포켓몬 이름 : " + name + " 스탯 : " + stat + " 히든 패시브 : " + hiddenPassive;
}
}
1️⃣ Step 1, 인스턴스 생성하기
앞서 알아본 Class 타입 가져오기를 통해서 어떤 클래스를 리플렉션 할지 정보를 가져와야 한다.
이후 getConstructor() 메서드를 통해서 생성자를 가져오고, newInstance()로 인스턴스를 생성한다.
public class Reflection {
public static void main(String[] args) throws Exception {
// 생성자 가져오기
Class<? extends Pokemon> pokemonClass = Pokemon.class;
Arrays.stream(pokemonClass.getConstructors()).forEach(System.out::println);
// 인스턴스 생성하기
Constructor<? extends Pokemon> constructor = pokemonClass.getConstructor();
Pokemon pokemon = constructor.newInstance();
System.out.println(pokemon);
// 인스턴스 생성하기 (all)
Constructor<? extends Pokemon> constructor2 =
pokemonClass.getConstructor(String.class, int.class, String.class);
Pokemon 피카츄 = constructor2.newInstance("피카츄", 50, "감전");
System.out.println(피카츄);
}
2️⃣ Step 2, 필드와 메소드 사용하기
리플렉션을 통해서 어떤 제약도 없이 필드와 메소드를 사용할 수 있다고 했다.
getDeclaredFields() 메서드와 getDeclaredMethod() 메소드를 통해서 필드와 메소드 정보를 가져와 사용할 수 있다.
메서드의 경우 invoke() 메서드를 통해서 직접 실행시켜 줘야 한다.
public class Reflection {
public static void main(String[] args) throws Exception {
Pokemon pokemon2 = new Pokemon("파이리", 100, "화상");
Class<?> pokemonClass2 = pokemon2.getClass();
// 리플렉션으로 필드 접근하기
Field[] fields = pokemonClass2.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.get(pokemon2));
}
fields[1].set(pokemon2, 90);
System.out.println(pokemon2);
// 리플렉션으로 메소드 접근하기
Method printName = pokemonClass2.getDeclaredMethod("printName", String.class);
printName.invoke(pokemon2, "꼬부기");
Method printHidden = pokemonClass2.getDeclaredMethod("printHidden");
printHidden.setAccessible(true);
printHidden.invoke(pokemon2);
}
}
주의할 점은 private로 선언된 필드나 메서드는 setAccessible() 메서드를 통해서 접근 권한을 true로 변경해야 한다.
🤔 왜 사용할까?
딱 보면 OOP를 위한 기술은 아니다, 외부에서도 내부를 볼 수 있기 때문이다. (캡슐화)
그럼에도 사용하는 이유는, 정적인 Java를 보완해 줄 수 있는 강력한 기술이라서 그렇다. 자세하게 알아보자!
1️⃣ 런타임 시점에도 알 수 있다
리플랙션을 사용하면 컴파일 단계에서 할 수 없는 것들을 할 수 있다.
- 알 수 없는 자원을 동적으로 사용
- 동적으로 클래스의 요소 검사
이는 컴파일 시점에서 알 수 없는 자원들 즉, 외부 라이브러리를 사용하는 상황에서 유용하다.
2️⃣ 자동 리소스 관리
리플렉션은 Spring과 같은 DI 프레임워크에서 자동으로 객체 생성 및 구성을 관리하는 데 사용된다.
스프링에서 Controller, Repository, Service 같은 컴포넌트들은 단순히 어노테이션만 붙여주면 바로 사용할 수 있다.
해당 클래스를 스프링한테 알려주지 않았는데도 다 알아서 관리 해주는 것도 리플렉션 때문이다.
런타임에 해당 어노테이션이 붙은 클래스를 찾은 뒤 리플랙션을 사용해서 인스턴스를 생성한다.
이후 필요한 정보를 주입하고 Bean Factory 즉 스프링에 저장해서 사용할 수 있는 것이다.
3️⃣ 더 나은 테스트 환경
리플렉션을 사용하면 private 또는 protected 접근 지정자를 사용한 필드와 메서드를 사용할 수 있다.
제한되어 있는 자원들을 접근할 수 있어 더 나은 테스트 범위를 허용하는 것이다.
😉 정리
이렇게 보면 엄청 대단한 기술일 수 있지만 캡슐화를 해치고 런타임 단계에서 알 수 있기 때문에 동작의 흐름을 알기는 어렵다. 하지만 동적으로 가능하게 한다는 것은 엄청 의미 있으므로 잘 알아보고 사용하자!😋😋
😋 지극히 개인적인 블로그지만 훈수와 조언은 제 성장에 도움이 됩니다 😋
'언어 공부 > Java' 카테고리의 다른 글
[Java] Optional 바르게 사용하기 (0) | 2023.06.22 |
---|---|
[Java] 빌드툴 (feat. Gradle) (0) | 2023.06.05 |
[Java] 멀티쓰레드 프로그래밍 (22.12.05 updated) (0) | 2022.12.05 |
[Java] Java 예외 처리 (2) | 2022.09.20 |
[Java] Java 인터페이스 (0) | 2022.09.08 |
댓글