상세 컨텐츠

본문 제목

리플렉션(Reflection) -3

Development/Java

by thisisnew 2019. 10. 29. 19:06

본문

반응형

 

이번 편에서는 리플렉션을 활용하여 변수의 정보를 얻은 후, 그것의 값을 동적으로 다루는 방법에 대해 알아보도록 하겠습니다.

 

2편에서 다룬 생성자를 동적으로 받아와서 객체를 생성하는 방법과, 메서드를 동적으로 활용하는 방법.

 

이 두 가지 방법을 기억하시나요. 이번에도 흡사한 패턴으로 진행할 것입니다.


변수의 값을 동적으로 제어하는 방법

먼저 1편에서 변수의 정보를 추출하는 방법으로 2가지의 메서드를 언급했었는데요.

 

바로 getFields()와 getDeclaredFields()였습니다.

 

뚜렷한 특징으로, 'Declared'라는 문자열이 붙은 함수는 private으로 선언된 인스턴스에도 접근할 수 있었죠.

 

1편에서 생성한 ReflectionTest 클래스에는 변수의 접근제어자를 전부 private으로 했기 때문에 getDeclaredFields() 메서드를 사용할 것입니다.

//ReflectionTest.java

package com.thisisnew.reflection;

public class ReflectionTest {
	
  private String name;
  private String gender;
  private Tool use;

  ...
}

다시 한번, ReflectionTest 클래스의 변수들을 확인하시고요.

 

이 중 문자열 타입의 name 변수를 이용하는 코드를 작성해 보도록 하겠습니다.

//GetReflectionTest.java

package com.thisisnew.reflection;

import java.lang.reflect.Field;

public class GetReflectionTest {
  public static void main(String[] args) {

    try {
        Class refClass = Class.forName("com.thisisnew.reflection.ReflectionTest");
        Field[] refField = refClass.getDeclaredFields();
        
        try {
         for(int i=0; i<refField.length; i++) {
           if(refField[i].getName().equals("name")) {
             System.out.println("name 발견!!");
           }
         }
        } catch (Exception e) {
           e.printStackTrace();
        }
        
    } catch (ClassNotFoundException e) {
    	e.printStackTrace();
    }
  }
}

2편의 메서드를 활용할 때 사용한 코드와 비슷하죠? 맞습니다. 원리는 같습니다.

 

반복문과 getDeclaredFields() 메서드를 이용하여 변수들에 접근 후, 조건문을 이용하여 name이라는 변수의 유무를 확인하는 코드입니다.

 

결과를 볼까요?

 

정상적으로 확인할 수 있었네요.

 

그럼 이제는 이 name의 값을 동적으로 다뤄보도록 하죠.

 

그러기 위해서는 2가지의 메서드를 사전에 알아야 하는데요.

  • get(Object obj) : 매게 변수 obj에 존재하는 클래스의 Field 객체가 가리키는 변수의 값을 Object의 타입으로 반환합니다. 
  • set(Object obj, Object value) : obj에 존재하는 클래스 변수의 값으로 설정합니다.
//GetReflectionTest.java

package com.thisisnew.reflection;

import java.lang.reflect.Field;

public class GetReflectionTest {
  public static void main(String[] args) {

    try {
        ReflectionTest reflectionTest = new ReflectionTest(); // get(), set()을 위한 객체 생성
        Class refClass = Class.forName("com.thisisnew.reflection.ReflectionTest");
        Field[] refField = refClass.getDeclaredFields();
        
        try {
         for(int i=0; i<refField.length; i++) {
           if(refField[i].getName().equals("name")) {
             System.out.println("name 발견!!");
           }
         }
        } catch (Exception e) {
           e.printStackTrace();
        }
        
    } catch (ClassNotFoundException e) {
    	e.printStackTrace();
    }
  }
}

먼저 두 메서드를 사용하기 위해 reflectionTest라는 이름의 객체를 하나 생성하겠습니다.

 

그리고 이제 변경하려는 name 변수가 private 이므로 setAccessible(true)를 설정해줘야겠죠?

//GetReflectionTest.java

package com.thisisnew.reflection;

import java.lang.reflect.Field;

public class GetReflectionTest {
  public static void main(String[] args) {

    try {
        ReflectionTest reflectionTest = new ReflectionTest(); 
        Class refClass = Class.forName("com.thisisnew.reflection.ReflectionTest");
        Field[] refField = refClass.getDeclaredFields();
        
        try {
         for(int i=0; i<refField.length; i++) {
           if(refField[i].getName().equals("name")) {
             Field field = refClass.getDeclaredField(refField[i].getName()); //field에 할당
             field.setAccessible(true);
             
           }
         }
        } catch (Exception e) {
           e.printStackTrace();
        }
        
    } catch (ClassNotFoundException e) {
    	e.printStackTrace();
    }
  }
}

이렇게 name의 값을 먼저 field라는 변수에 할당한 후 허용을 해주시면 됩니다.

//GetReflectionTest.java

package com.thisisnew.reflection;

import java.lang.reflect.Field;

public class GetReflectionTest {
  public static void main(String[] args) {

    try {
        ReflectionTest reflectionTest = new ReflectionTest(); 
        Class refClass = Class.forName("com.thisisnew.reflection.ReflectionTest");
        Field[] refField = refClass.getDeclaredFields();
        
        try {
         for(int i=0; i<refField.length; i++) {
           if(refField[i].getName().equals("name")) {
             Field field = refClass.getDeclaredField(refField[i].getName());
             field.setAccessible(true);
             
             field.set(reflectionTest, "Thisisnew"); // 값 설정
             Object fieldValue = field.get(reflectionTest);// 값 받아오기
             System.out.println(fieldValue);
           }
         }
        } catch (Exception e) {
           e.printStackTrace();
        }
        
    } catch (ClassNotFoundException e) {
    	e.printStackTrace();
    }
  }
}

그 후 set() 메서드에 name 변수가 자리하는 reflection 객체와, 변경하고 싶은 값(여기서는 "Thisisnew")을 담아서 설정해주도록 하고요.

 

get() 메서드는 반환형이 Object가 되도록 하여 다시 받아오게끔 작성하면 됩니다. 간단하죠?

 

이제 결과를 보도록 하겠습니다.

 

의도한 대로 값이 정확히 설정됨을 확인할 수 있네요.

 


리플렉션을 이용하면 코드를 좀 더 유연하게 만들 수 있습니다.

 

하지만 setAccessible(true)와 같은 메서드의 사용으로 인해 캡슐화를 깨뜨리는 일이 생길 수 있고요.

 

또, 리플렉션이라는 기능이 사용하는 JVM의 Perm 메모리는 용량의 한계가 있습니다.

 

이런 점들을 고려하여 코드를 설계하시면 더욱 좋을 것 같네요.

 

감사합니다.

반응형

관련글 더보기

댓글 영역