자바의 리플렉션
http://ktko.tistory.com/152?category=629345 에서 리플렉션에 대한 개념? 에 대해 잠깐 포스팅하였습니다.
자바 리플렉션 어디에 쓸까 ? 프로젝트 런타임에 클래스의 구조를 코딩으로 파악이 가능하니 자동 매핑을 해주는 기능들은 다 Reflection을 사용하고 있습니다. 당연히 퍼포먼스가 좋지 않고 남발하게 되면 디버깅이나 가독성에서 극악의 환경을 만든다고 합니다. 실제로 스프링 프레임워크에서도 자동 매핑과 여러 기능을 쓰기 위해 리플렉션을 사용한다고 한다. 자동 매핑을 하려면 클래스의 필드가 모가있는지, 필드 값이 무엇인지 알아야 하는데 어떤 방식으로 접근하는지 간략하게 작성해보았습니다.
샘플 소스는 다 메소드로 구현했으며 메소드 안에 있는 코드를 활용하면 됩니다. 이해를 위해 간단히 몇 개의 클래스를 만들어 보았습니다.
class KTKO_INFO{ public String NAME = "KTKO"; public String JOB = "PROGRAMMER"; public void getInfo(){ System.out.println("NAME : " + NAME + " JOB : " + JOB); } public void talk(){ System.out.println("Hello Refelection"); } } class Calculate { public int sum(int a, int b) { return a + b; } } class ParentField { private String parentPri; protected String parentPro; public String parentPub; public ParentField() { } } class ChildField extends ParentField { private String pri; protected String pro; public String pub; public ChildField() { } public ChildField(String pri, String pro, String pub) { this.pri = pri; this.pro = pro; this.pub = pub; } }
클래스의 메소드 가져오기
//메소드 가져오기. public static void getMethod() { try { List<String> a = new ArrayList<>(); Class c = Class.forName("java.util.ArrayList"); Method m[] = c.getDeclaredMethods(); for (int i = 0; i < m.length; i++) { System.out.println(m[i].getName()); } } catch (Throwable e) { System.out.println(e); } }
클래스의 필드명 가져오기
//Field 명 가져오기 public static void getField() { try { Class KTKO = Class.forName("test11.KTKO_INFO"); Field[] ktko_field = KTKO.getDeclaredFields(); for (int i = 0; i < ktko_field.length; i++) { System.out.println(ktko_field[i].getName()); } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
클래스의 필드 값, 함수 콜 하기
//필드의 값과 함수를 콜 하기 public static void callFieldOrMethod() { try { Class KTKO = Class.forName("test11.KTKO_INFO"); Object obj = KTKO.newInstance(); Field[] field = KTKO.getDeclaredFields(); Method[] methods = KTKO.getDeclaredMethods(); for(int i = 0; i < field.length; i++) { System.out.println(field[i].get(obj)); } for(int i = 0; i < methods.length; i++) { methods[i].invoke(obj, null); } } catch (Exception e) { e.printStackTrace(); } }
필드 값 변경하기
//필드 값 변경하기 public static void changeFieldValue() { try { KTKO_INFO ktko = new KTKO_INFO(); Class cls = Class.forName("test11.KTKO_INFO"); Field field = cls.getDeclaredField("NAME"); System.out.println(field.get(ktko)); field.set(ktko, "KKT"); System.out.println(field.get(ktko)); }catch(Exception e) { e.printStackTrace(); } }
파라미터 값을 전달하여 메소드 실행하기
//이름으로 메소드를 실행하기 public static void callNameMethod() { try { Class calculate = Class.forName("test11.Calculate"); Class partypes[] = new Class[2]; partypes[0] = Integer.TYPE; partypes[1] = Integer.TYPE; Method meth = calculate.getMethod("sum", partypes); Calculate methobj = new Calculate(); Object arglist[] = new Object[2]; arglist[0] = new Integer(11); arglist[1] = new Integer(12); Object retobj = meth.invoke(methobj, arglist); Integer retval = (Integer) retobj; System.out.println(retval.intValue()); } catch (Exception e) { e.printStackTrace(); } }
필드 값 GET, SET 하기
Reflection은 public으로 선언된 필드 값만 가지고 올 수 있다.
//필드 값 Get, Set public static void getANDSetFieldValue() { ChildField ft = new ChildField(); Class ftClass = ft.getClass(); try { Field f1 = ftClass.getField("pub"); f1.set(ft, "reflecting on life"); String str1 = (String) f1.get(ft); System.out.println("pub field: " + str1); Field f2 = ftClass.getField("parentPub"); f2.set(ft, "again"); String str2 = (String) f2.get(ft); System.out.println("\nparentPub field: " + str2); } catch( Exception e) { e.printStackTrace(); } try { System.out.println("이제 NoSuchFieldException Exception 발생합니다.!!"); Field f3 = ftClass.getField("pro"); } catch (Exception e) { e.printStackTrace(); } }
이 외에 Constructor을 이용해서 Reflection을 사용할 수도 있고 다양한 방법이 있었습니다.
http://www.avajava.com/tutorials/categories/classes-and-objects 에 들어가면 Reflection에 대한 다양한 예제들을 확인할 수 있습니다.
리플렉션에 대해 고민해야할 것
1. Reflection을 사용한 코드는 느리다
개발자들 사이에 공공연히 진실로 받아들여지는 이 말은 사실이 아닙니다. 적절히 사용한 Reflection은 오히려 성능을 향상시킬 수 있으며, 또한 많은 이득을 제공합니다.
2. Reflection을 이용하여 개발한 프로그램은 에러가 발생하기 쉽고 디버깅이 어렵다
Reflection은 컴파일 시 타입 체킹을 할 수 없습니다. 따라서, 런타임시 잘못된 파라미터로 인해 런타임 에러가 발생하기 쉽습니다. 하지만, 적절히 사용된 런타임 에러 메시지를 이용해 충분히 디버깅이 쉬운 환경으로 만들 수 있습니다.
Reflection을 사용한 코드는 일반적인 객체 생성, 메서드 호출 코드에 비교하면 복잡한 것이 사실이지만 클래스의 타입을 비교하여 객체를 생성하는 코드의 경우, 대량의 if/else문을 사용하는 것보다 Reflection을 이용하여 재사용 가능한 컴포넌트로 만든다면, 오히려 코드를 단순화할 수 있습니다.
리플렉션을 활용할만한 코드가 모가 있을까 하다가 https://steemit.com/kr-dev/@nhj12311/2-java-reflection 를 보았는데 좋은 예제 코드를 발견해서 링크를 걸어놓았다.
참고 자료
https://kmongcom.wordpress.com/2014/03/15/%EC%9E%90%EB%B0%94-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98%EC%97%90-%EB%8C%80%ED%95%9C-%EC%98%A4%ED%95%B4%EC%99%80-%EC%A7%84%EC%8B%A4/
http://gyrfalcon.tistory.com/entry/Java-Reflection
https://steemit.com/kr-dev/@nhj12311/2-java-reflection
'Java 개발 이야기' 카테고리의 다른 글
자바 이미지 리사이즈 (0) | 2018.04.19 |
---|---|
자바의 객체 비교 Comparable와 Comparator (0) | 2018.04.18 |
자바 리플렉션(Reflection) 개념 (1) | 2018.04.17 |
자바 예외처리(Exception handling) (0) | 2017.09.21 |
자바 객체의 직렬화(Serializable, serialVersionUID) (0) | 2017.09.19 |