본문 바로가기
자바

[Java] 런타임에 자바 코드를 조작하는 방법: 리플렉션(Reflection)

by 책 읽는 개발자_테드 2021. 12. 10.
반응형

· 리플렉션은 런타임에 메서드, 클래스, 인터페이스의 동작을 검사하거나 수정하는데 사용되는 API다.  

· 리플렉션에 필요한 클래스는 java.lang.reflect 패키지에서 제공된다.

· 리플렉션을 사용하면 특정 객체가 속한 클래스에 대한 정보, 객체를 사용하여 실행할 수 있는 해당 클래스의 메서드에 대한 정보를 얻을 수 있다.

· 리플렉션을 사용하면 접근 제어자(private, public 등)와 상관없이 런타임에 메서드를 호출할 수 있다.

· 사용 예시:

   - JSON 파일의 속성을 Jackson, GSON, BOON 등과 같은 자바 객체의 getter/setter 메서드에 매핑할 수 있다. 

   - JDBC ResultSet의 열 이름을 Java 객체의 getter/setter 메서드에 매핑할 수 있다.

 

 

Class


Class 객체를 얻는 방법

클래스에 대한 검사를 수행하려면 java.lang.class 객체를 가져와야 한다. 상황에 따라 여러 방법으로 Class 객체를 가져올 수 있다.

 

컴파일 타임에 클래스 이름을 아는 경우

 

Class aClass = Person.class;

 

컴파일 타임에 클래스 이름을 모르는 경우

Class.forName(String className) 메서드를 사용하여 런타임에 클래스 이름으로 Class 객체를 가져올 수 있다.

이때, 클래스 이름으로 FQCN(Fully Quailfied Class Name)을 제공해야한다. 즉, 패키지 경로와 클래스 이름을 모두 제공해야한다.

 

String className = "reflectionStudy.Person";
Class aClass = Class.forName(className);

  

클래스 이름

Class 객체를 통해 클래스 이름을 얻을 수 있다.

 

패키지를 포함한 이름 얻기

Class aClass = Person.class;
String className = aClass.getName();

 

패키지를 포함하지 않고 이름 얻기

Class aClass = Person.class;
String simpleClassName = aClass.getSimpleName();

 

제어자(Modifier)

Class 객체를 통해 클래스 제어자에 접근할 수 있다. 클래스 제어자 public, private, static 등의 키워드를 말한다.

Class aClass = Person.class;
int modifiers = aClass.getModifiers();

 

제어가는 각 제어자가 설정되거나 지워지는 플래그 비트인 int(위 코드의 modifiers)로 압축된다. 

java.lang.reflect.Modifier 클래스에서 boolean을 리턴하는 다음 메서드를 사용해서 제어자가 사용되었는지 확인할 수 있다.

 

 

Modifier.isAbstract(modifiers);
Modifier.isFinal(modifiers);
Modifier.isInterface(modifiers);
Modifier.isNative(modifiers);
Modifier.isPrivate(modifiers);
Modifier.isProtected(modifiers);
Modifier.isPublic(modifiers);
Modifier.isStatic(modifiers);
Modifier.isStrict(modifiers);
Modifier.isSynchronized(modifiers);
Modifier.isTransient(modifiers);
Modifier.isVolatile(modifiers);

 

 

패키지 정보

Class 객체를 통해 패키지 정보를 얻을 수 있는 java.lang.Package 클래스 객체를 얻을 수 있다.

 

Class aClass = Person.class;
Package aPackage = aClass.getPackage();

 

수퍼 클래스

Class 객체를 통해 클래스의 수퍼 클래스에 접근할 수 있다.

 

Class aClass = Person.class;
Class superClass = aClass.getSuperclass();

 

구현된 인터페이스

Class 객체를 통해 클래스에 구현된 인터페이스 목록을 얻을 수 있다.

 

Class aClass = Person.class;
Class[] interfaces = aClass.getInterfaces();

 

단, Class 객체를 가져온 클래스에는 명시적으로 구현하지 않았지만, 해당 클래스의 수퍼 클래스에 구현된 인터페이스는 반환되지 않는다.

 

생성자 정보

Class 객체를 통해 생성자 목록을 얻을 수 있다.

 

Class aClass = Person.class;
Constructor[] constructors = aClass.getConstructors();

 

메서드 정보

Class 객체를 통해 메서드 목록을 얻을 수 있다.

 

Class aClass = Person.class;
Method[] method = aClass.getMethods();

 

필드 정보

Class 객체를 통해 필드 목록을 얻을 수 있다.

 

Class aClass = Person.class;
Field[] fields = aClass.getFields();

 

애노테이션 정보

Class 객체를 통해 클래스 애노테이션 목록을 얻을 수 있다.

 

Class aClass = Person.class;
Annotation[] annotations = aClass.getAnnotations();

 

Method


Method 객체를 얻는 방법

Method 클래스는 Class 객체로부터 얻을 수 있다.

 

Class aClass = Person.class;
Method[] methods = aClass.getMethods();

 

Method[] 배열에는 클래스에 선언된 각각의 public 메서드의 인스턴스가 존재한다.

 

 

컴파일 타임에 메서드 매개변수 유형을 아는 경우

접근하려는 메서드의 정확한 매개변수 유형을 알고 있는 경우, 모든 메서드 배열을 가져오는 대신 특정한 하나의 메서드 인스턴스만 가져올 수 있다. 

 

Class aClass = Person.class;
Method method = aClass.getMethod("setName", new Class[]{String.class});

 

이때, 주어진 메서드 이름과 매개변수와 일치하는 메서드가 없는 경우 NoSuchMethodException이 발생한다.

 

접근하려는 메서드가 매개변수를 사용하지 않는 경우 null을 매개변수 유형 배열로 전달한다.

 

Class aClass = Person.class;
Method method = aClass.getMethod("getName", null);

 

메서드 매개변수 및 리턴 타입

 

다음과 같이 메서드 매개변수에 접근할 수 있다.

 

Method method = aClass.getMethod("setName", new Class[]{String.class});
Class[] parameterTypes = method.getParameterTypes();

 

다음과 같이 메서드 리턴 타입에 접근할 수 있다. 

 

Method method = aClass.getMethod("setName", new Class[]{String.class});
Class returnType = method.getReturnType();

 

Method 객체를 사용하여 메서드 호출하기

다음과 같이 Method  객체의 invoke() 메서드를 통해 특정 클래스의 메서드를 호출할 수 있다.

 

Person person = new Person("ted",5);
Method method = aClass.getMethod("getName", null);
Method method2 = aClass.getMethod("setAge", int.class);
Method method3 = aClass.getMethod("getAge");

String returnName = (String)method.invoke(person); // 이름 조회
method2.invoke(person,13);             // 나이 수정
Integer returnAge = (Integer)method3.invoke(person);  // 나이 조회

System.out.println("이름:"+returnName + ", 나이:"+returnAge);

 

Person person = new Person("ted",5);
Method method = aClass.getMethod("getName", null);
Method method2 = aClass.getMethod("setAge", int.class);
Method method3 = aClass.getMethod("getAge");

String returnName = (String)method.invoke(person); // 이름 조회
method2.invoke(person,13);             // 나이 수정
Integer returnAge = (Integer)method3.invoke(person);  // 나이 조회

System.out.println("이름:"+returnName + ", 나이:"+returnAge);

 

public class Person extends Animal implements Head {
   public String name;
   public int age;

   public Person(String name, int age){
      this.name = name;
      this.age = age;
   }

   public String getName() {
      return name;
   }
   public void setName(String name) {
      this.name = name;
   }
   public void setAge(int age) {
      this.age = age;
   }
   public int getAge() {
      return age;
   }
}

 

invoke() 메서드의 첫 번째 매개변수는 메서드를 호출할 객체(정적 메서드일 경우 null로 대체 가능),

두 번째 부터는 메서드 매개변수를 의미한다.

 

실행 결과

 

Field


Field 객체를 얻는 방법

Field 클래스는 Class 객체로 부터 얻을 수 있다.

 

Class aClass = Person.class;
Field[] fields = aClass.getFields();

 

Field[] 배열은 클래스에 선언된 각각의 public 필드에 대한 인스턴스를 갖는다.

 

컴파일 타임에 접근하려는 Field의 이름을 알고 있는 경우 

Class aClass = Person.class;
Field field = aClass.getField("age");

 

Field 값 조회 및 설정

Field 참조를 얻으면 Field.get(), Field.set() 메서드를 사용하여 값을 조회 및 설정할 수 있다.

 

Class aClass = Person.class;
Field field = aClass.getField("age");

Person person = new Person("ted",5);
Object value = field.get(person);
field.set(person, value);

System.out.println(field.getName());

 

참고

http://tutorials.jenkov.com/java-reflection/fields.html

http://tutorials.jenkov.com/java-reflection/methods.html

http://tutorials.jenkov.com/java-reflection/index.html

http://tutorials.jenkov.com/java-reflection/classes.html

https://www.geeksforgeeks.org/reflection-in-java/

반응형

댓글