Java Reflection 이 뭐지?
JVM 기능
런타임 동안 클래스와 객체 정보를 추출할 수 있음
Reflection API로 접근 가능
JDK에 포함된 클래스 집합
Reflection이 할 수 있는 것
소스 코드를 수정하지 않고 새로운 프로그램 순서를 만들 수 있음.
실행하고 있는 클래스와 객체에 동적으로 코드 삽입.
실제로 사용하는 곳
로깅 프레임워크
ORM
Spring Framework
JUnit
Jackson
Spring Framework
@Autowired
@Configuration
@Bean
java 복사 @Configuration
public class Config {
@Bean
public Connection createConnection () {
//..
}
@Bean
public Session createSession () {
//..
}
}
java 복사 public class Client {
@Autowired
public Client (Connection connection, Session session) {
this .connection = connection;
this .session = session;
}
public void invoke () {
//..
}
}
bean을 정의하고 의존성을 주입하는 과정이 reflection 을 통해 이뤄짐.
Jackson library
Reflection 을 사용해 클래스를 확인하고 필드를 분석한다.
java 복사 public class Listen {
private String server;
private int port;
private String upstream;
private List<String> alias;
// getter..
// setter..
}
json 복사 {
"server" : "canxan" ,
"port" : 443 ,
"upstream" : "192.168.1.1"
"alias" :[
"canxan.com" ,
"www.canxan.com"
]
}
json 복사 Listen listen = objectMapper.readValue("..." , Listen.class);
String listenJson = objectMapper.writeValueAsString(listen);
Reflection 을 사용하면서 주의해야 할 점
런타임에서 보이지 않는 구조에 접근할 수 있음
목적에 맞지 않게 사용되면 코드를 변경해야 하는데 코드가 늦게 실행되거나 실행되면 위험한 상황이 발생함
보통 reflection에서 문제가 발생하면 어플리케이션에 심각한 문제가 생김
"With great power, comes great responsibility"
Class<?> 오브젝트
객체의 런타임에 관한 정보를 포함하고 있음
어플리케이션의 클래스 정보
메서드와 필드 정보
상속 또는 인터페이스의 정보
Class<?> 에 접근하기
1. Object.getClass()
java 복사 String stringObject = "아이유" ;
Singer singer = new Singer ();
Map<String, Integer> map = new HashMap<>();
Class<String> stringClass = stringObject.getClass ();
Class<Singer> singerClass = singer.getClass ();
Class<?> mapClass = map.getClass ();
마지막의 Map 객체는 인터페이스가 아니라 HashMap 클래스를 나타냄.
변수의 런타임 타입이기 때문임.
Primitive 타입은 객체가 아님.
2. 타입 이름에 '.class' 붙이기
클래스에 인스턴스가 없을 때 사용됨
이때 primitive 타입에 대해서 얻을 수 있음.
java 복사 Class booleanType = boolean .class ;
Class intType = int .class ;
Class doubleType = double .class ;
메서드 파라미터나 클래스 맴버를 확인할때 원시타입일 수 있기 때문에.
3. Class.forName()
정적 메서드
동적으로 패키지명이 포함된 클래스를 찾을 수 있음.
Primitive 타입은 여전히 사용할 수 없음
잘못 입력하는 경우 ClassNotFoundException 발생
이때가 가장 위험함.
java 복사 Class<?> stringType = Class.forName ("java.lang.String" );
Class<?> singerType = Class.forName ("artist.Singer" );
Class<?> genreType = Class.forName ("artist.Singer$Genre" );
package artist;
class Singer {
static class Genre {
//..
}
}
사용자 정의 구성 파일 (예 : bean xml) 에서 전달될때 많이 사용 됨.
xml 복사 <bean id="artist" class="artist.Singer" >
<property name="gender" value="woman" />
</bean>
java 복사 Class<?> artist = Class.forName (value);
외부 라이브러리인 경우 컴파일 할때 사용되기도 함.
컴파일 후 실행 시 해당 클래스가 어플리케이션의 클래스 경로에 추가됨.
Java wildcard
java 복사 Class<?>
Java 제네릭에서는 Integer 클래스는 Number 클래스를 상속받고, Stirng 클래스는 CharSequence 클래스를 구현하지만 List<Integer>는 List<Number>를 상속 받는게 아님.
그러나 wildcard 라고 부르는 List<?>는 제네릭 타입이면 전부 해당하는 상위 타입임.
즉, List<T>는 List<?>를 상속받음.
이는 Class<T> 와 Class<?> 관계도 마찬가지임!
Class<?>를 사용하면 모든 파라미터 타입의 클래스 객체를 설명할 수 있다.
Class<?>는 모든 타입의 Class<T>에 대한 상위 타입임.
언제 쓰는건데?
컴파일 하는 동안 컴파일러가 제네릭 타입을 정확하게 알 수 없을 때
java 복사 Class<?> singerType = Class.forName ("artist.Singer" );
클래스의 제네릭 파라미터가 제네릭 타입이라면 wildcard를 사용해야 함.
java 복사 Map<String, Integer> map = new HashMap<>();
Class<Singer> singerClass = singer.getClass ();
wildcard 를 사용해서 클래스가 메서드로 전달되는걸 제한할 수 있음
java 복사 // Collection 을 상속받는 타입으로만 제한
public List<String> findAllMethods (Class<? extends Collection> clazz) {
//..
}
Class<?> 사용해보기
java 복사 package o.e._01;
import java.util.HashMap;
import java.util.Map;
public class FirstReflection {
public static void main (String[] args) throws ClassNotFoundException {
Class<String> stringClass = String.class ;
Map<String, Integer> mapObject = new HashMap<>();
Class<?> hashMapClass = mapObject.getClass ();
Class<?> squareClass = Class.forName ("o.e._01.FirstReflection$Square" );
printClassInfo (stringClass, hashMapClass, squareClass);
}
private static void printClassInfo (Class<?> ... classes) {
for (Class<?> clazz : classes) {
System.out.println (String.format ("클래스 이름 : %s, 클래스 패키지 명 : %s" ,
clazz.getSimpleName (),
clazz.getPackageName ()));
Class<?>[] implementedInterfaces = clazz.getInterfaces ();
for (Class<?> implementedInterface : implementedInterfaces) {
System.out.println (String.format ("%s 클래스 implement : %s" ,
clazz.getSimpleName (),
implementedInterface.getSimpleName ()));
}
System.out.println ();
System.out.println ();
}
}
private static class Square implements Drawable {
@Override
public int gerNumberOfCorners () {
return 4 ;
}
}
private static interface Drawable {
int gerNumberOfCorners ();
}
private enum Color {
BLUE,
RED,
GREEN
}
}
복사 > Task :FirstReflection.main()
클래스 이름 : String, 클래스 패키지 명 : java.lang
String 클래스 implement : Serializable
String 클래스 implement : Comparable
String 클래스 implement : CharSequence
String 클래스 implement : Constable
String 클래스 implement : ConstantDesc
클래스 이름 : HashMap, 클래스 패키지 명 : java.util
HashMap 클래스 implement : Map
HashMap 클래스 implement : Cloneable
HashMap 클래스 implement : Serializable
클래스 이름 : Square, 클래스 패키지 명 : o.e._01
Square 클래스 implement : Drawable
Map의 경우 런타임에서 인터페이스가 아닌 HashMap클래스가 됨.
java 복사 var circleObject = new Drawable () {
@Override
public int gerNumberOfCorners () {
return 0 ;
}
};
printClassInfo (stringClass, hashMapClass, squareClass,
Collection.class , boolean .class , int [][].class , Color.class ,
circleObject.getClass ());
//...
private static void printClassInfo (Class<?> ... classes) {
for (Class<?> clazz : classes) {
System.out.println (String.format ("클래스 이름 : %s, 클래스 패키지 명 : %s" ,
clazz.getSimpleName (),
clazz.getPackageName ()));
Class<?>[] implementedInterfaces = clazz.getInterfaces ();
for (Class<?> implementedInterface : implementedInterfaces) {
System.out.println (String.format ("%s 클래스 implement : %s" ,
clazz.getSimpleName (),
implementedInterface.getSimpleName ()));
}
System.out.println ("배열 : " + clazz.isArray ());
System.out.println ("원시타입 : " + clazz.isPrimitive ());
System.out.println ("열거타입 : " + clazz.isEnum ());
System.out.println ("인터페이스 : " + clazz.isInterface ());
System.out.println ("익명클래스 : " + clazz.isAnonymousClass ());
System.out.println ();
System.out.println ();
}
}
복사 클래스 이름 : Collection, 클래스 패키지 명 : java.util
Collection 클래스 implement : Iterable
배열 : false
원시타입 : false
열거타입 : false
인터페이스 : true
익명클래스 : false
클래스 이름 : boolean, 클래스 패키지 명 : java.lang
배열 : false
원시타입 : true
열거타입 : false
인터페이스 : false
익명클래스 : false
클래스 이름 : int[][], 클래스 패키지 명 : java.lang
int[][] 클래스 implement : Cloneable
int[][] 클래스 implement : Serializable
배열 : true
원시타입 : false
열거타입 : false
인터페이스 : false
익명클래스 : false
클래스 이름 : Color, 클래스 패키지 명 : o.e._01
배열 : false
원시타입 : false
열거타입 : true
인터페이스 : false
익명클래스 : false
클래스 이름 : , 클래스 패키지 명 : o.e._01
클래스 implement : Drawable
배열 : false
원시타입 : false
열거타입 : false
인터페이스 : false
익명클래스 : true