소프트웨어 개발/Java - Basic

Java Reflection (리플렉션) 을 이용해 private 특정 타입 필드를 죄다 반올림하기.

늘근이 2016. 4. 17. 12:19

밥먹기전에 이러한 메서드는 필요할것 같아서 쓴다.


일단, 한 객체에 들어있는 수치값이 한 200개가 있다고 치자. 근데 거기중에 int타입도 있고 double타입도 있고, String타입도 있다.

만약 double타입의 값들을 모두 가져와서 두자리수로 반올림 하고 싶으면 대체 어떻게 하여야 하는가? 하나하나 하드코딩으로 double필드만 골라서 그걸 반올림하고 있을 것인가?

안된다.. 그렇다면 주말에 나와서 코딩을 해야하고 추후에 어떤 거지같은 경우가 생길지도 모른다. 물론 회사에 늦게 남아있으면 어쨌든간 열심히 하는것처럼 보이니 알아서 하도록 한다.


어쨌든 먼저 자바는 2자리수로 반환하는 Math안의 함수가 없어 보인다.

하나 간단하게 만들도록 한다.



2자리 수 변환 함수


  public static double convertDecimal2 (double number){
  
//    DecimalFormat format_2Places = new DecimalFormat("0.00");
//      return Double.valueOf(format_2Places.format(number));
  
  return (double) Math.round(number * 100.0) / 100.0;
  
 }


주석처리된 부분은, 조금 다르게 포멧을 이용해 숫자를 수정하는 부분이다. 그냥 버린다.

이제 시험용 클래스를 아래와 같이 만들어보도록 한다.


여러 타입을 포함하는 클래스 ClassWithDouble


 class ClassWithDouble {
 
 private String id;
 private double var1;
 private double var2;
 private int var3;
 
 public String getId() {
  return id;
 }
 public void setId(String id) {
  this.id = id;
 }
 public double getVar1() {
  return var1;
 }
 public void setVar1(double var1) {
  this.var1 = var1;
 }
 public double getVar2() {
  return var2;
 }
 public void setVar2(double var2) {
  this.var2 = var2;
 }
 public int getVar3() {
  return var3;
 }
 public void setVar3(int var3) {
  this.var3 = var3;
 }
 

}


보다시피, double형이 다른 타입과 섞여있다. 다만 실무에서는 저런 필드가 좌르륵 100개가 늘어져있을 수 있기 때문에 그점을 가정한다. 사실 간단한 클래스라면 리플렉션보다는 그냥 getter setter를 통해서 숫자를 수정하는 편이 훨 시간을 아낀다.

그리고 한가지 중요한게 public 이 아닌 private으로 설정되어있다는 뜻이다. 솔직히 getter setter를 어차피 똑같은 스콥으로 만든다면 굳이 private으로 설정해야하는지 좀 짜증나지만 많은 회사들이 그렇게 코딩을 하고 가르침당하고 있으니 관습을 따른다.

어쨌든 private은 reflection을 이용할때 다른 메서드를 써야한다.

어쨌든 좀더 난이도가 높기때문에 private으로 바꿨다. 껄껄


이제 객체를 생성해서 한번 값을 먼저 설정해보도록 한다.


 초기값설정


 public static void main(String[] args) {
  ClassWithDouble c = new ClassWithDouble();
  c.setId("고락가락");
  c.setVar1(1.2141200000);
  c.setVar2(24.1234123);
  c.setVar3(100);

}


이제 객체가 생성되었다. var1 과 var2 에는 소수점이 지저분하게 늘어져있을 것이다. 출력은 귀찮으니 하지않겠다. 잘들어가있을거라고 믿고,


이제, 클래스 안에있는 field 들을 한번 가져와 본다.


선언한 field가져오기

Field[] fields = c.getClass().getDeclaredFields();


중요한건, getDeclaredFields 는 private필드를 포함한 모든걸 죄다 가져오고, getFields 는 접근제한자에 위배되지 않는 놈들만 가져온다는 것이다.

이는 나름대로 중요한 의미를 가지는데,

접근제한자로 아무리 막아놔도 리플렉션을 이용해서는 자유자재로 필드를 훔쳐보고 값을 바꿀수 있다는 것이다. 바로 이 리플렉션이 꼭 작동해야하는 프로그램이 있는데 바로 IDE라고 할수 있겠다. private 필드를 멋도 모르고 수정하려 했을때 콘솔에서 '접근제한자로 막혀있습니다 저리가세요' 라고 안내문이 뜬다면 이는 이클립스와 같은 IDE가 어떤 방식으로든 접근대상에 대해 파악하고 있다는 뜻인데 내부적으로 reflection을 이용할수밖에 없는것이다.

어쨌든 쓸데없는 소리였고


이제 fields안에 private필드들도 고스란히 담겨있으니 루프를 돌면서 수정을 해본다.


값바꾸기

  for (Field f : fields) {

   if (f.getType().toString().equals("double")) {

    try {
     f.setAccessible(true);
     double a = f.getDouble(c);
     f.setDouble(c, convertDecimal2(a));

    } catch (Exception e) {
     e.printStackTrace();
    }

   }

  }


별거없다. f의 타입을 받아서 double형인지 확인한 다음에, f.setAccessible()을 통해 Access Modifier에서 선언한 private을 접근가능하게 바꿔버린다.

그리고 double값을 가져와서 위에서 메서드로만든 convertDecimal2로 두자리 반올림을 한다음에 다시 setDouble메서드로 값을 바꾼다.


그리고 다음을 이용해 출력을 해보자.


  System.out.println(c.getId());
  System.out.println(c.getVar1());
  System.out.println(c.getVar2());
  System.out.println(c.getVar3());


결과는 다음과 같다.


고락가락
1.21
24.12
100



 

아주 예쁘게 두자리수로 변환되었다.