자바의 리플렉션을 이용하면 클래스의 정보를 얻는 것 외에도, 해당 정보를 통해 객체를 생성하거나 메서드를 실행시키는
행위 등을 할 수 있습니다.
이번 포스팅에서는 리플렉션을 이용한 객체 생성과 메서드 실행을 진행해보겠습니다.
객체 생성
자바는 new 연산자와 생성자를 통해 객체를 생성합니다. 그러므로 먼저 생성자(Constructor)를 얻어와야 합니다.
생성자 목록은 아래와 같이 얻을 수 있습니다.
Constructor[] constructors = testClass.getConstructors();
특정 생성자를 얻는 방법입니다. 인자로 생성자의 파라미터를 전달해주면 됩니다.
가변 인자를 받고 있으므로 2개 이상일 경우 계속 나열해주면 됩니다. 아니면 배열로 전달해줘도 되구요.
예시)
Constructor constructor = testClass.getConstructor(String.class); // 파라미터 1개
Constructor constructor = testClass.getConstructor(new Class[]{String.class, String.class, Integer.class});
// 파라미터 2개 이상
(인자로 전달한 파라미터에 맞는 생성자가 없으면 NoSuchMethodException이 발생합니다.)
생성자를 얻어왔으면 객체를 생성하는 법은 간단합니다.
Consturctor 클래스의 newInstance(Object... initargs) 를 사용하시면 됩니다.
그럼 User 클래스를 리플렉션을 이용해서 생성해보겠습니다.
public static void main(String[] args) throws Exception{ Class userClass = User.class; Constructor cs = userClass.getConstructor(new Class[]{String.class, String.class, String.class, Integer.class}); User user = (User)cs.newInstance("joontID","joontPWD","joont",920000); System.out.println(user.toString()); }
newInstance의 반환형이 Object라 캐스팅이 필요합니다.
또한 newInstance시에 인자의 개수가 위에서 얻어온 Constructor의 파라미터 개수와 맞지 않을 경우
IllegalArgumentException이 발생합니다.
잘 생성되었고 toString() 메서드 또한 잘 실행됨을 보실 수 있습니다.
이번엔 접근제어자가 private인 생성자를 통해 객체를 생성해보겠습니다.
앞선 장에서 getConstructor등의 메서드는 접근제어자가 public인 속성만 가져올 수 있다고 했었습니다.
테스트를 한번 해보겠습니다.
public class User { public String id; public String pwd; public String name; public Integer birthDate; // private 생성자 private User(String id, String pwd){ this.id = id; this.pwd = pwd; } // ..... }
User 클래스에 private 생성자를 추가하였습니다.
리플렉션을 통해 해당 생성자를 가져와보겠습니다.
public static void main(String[] args) throws Exception{ Class userClass = User.class; Constructor cs = userClass.getConstructor(new Class[]{String.class, String.class}); User user = (User)cs.newInstance("joontID","joontPWD"); System.out.println(user.toString()); }
객체생성도 전에 NoSuchMethodException이 발생합니다. 해당 생성자를 못찾는거죠..
그렇다고 불가능한것은 아닙니다 ㅎㅎ 캡슐화에 어긋나는 행위이긴 하지만 여러 프레임워크에서 유용하게 사용됩니다.
앞선 장에서 언급했던 getDeclaredXXX()를 사용하는 방법입니다.
public static void main(String[] args) throws Exception{ Class userClass = User.class; Constructor cs = userClass.getDeclaredConstructor(new Class[]{String.class, String.class}); cs.setAccessible(true); // 중요. access 가능하도록 변경 User user = (User)cs.newInstance("joontID","joontPWD"); System.out.println(user.toString()); }
여기서 중요한 부분은 setAccessible(boolean flag) 입니다.
getDeclaredConstructor(Object... args)를 통해 private 생성자를 얻어올 순 있으나 기본적으로 접근이 제한되어 있으므로
setAccessible을 true로 해주지 않으면 IllegalAccessException이 발생합니다.
setAccessible을 true로 설정하고 객체를 생성하면 잘 생성됨을 보실 수 있습니다.
메서드 호출
메서드호출 또한 객체 생성과 비슷합니다. 일단 먼저 클래스의 메서드를 얻어와야겠네요.
메서드 목록 얻는 방법입니다.
Method[] methods = testClass.getMethods();
특정 메서드를 얻는 방법입니다. 인자로 메서드의 이름과 파라미터 타입을 전달해주면 됩니다. (오버로딩 때문)
예시)
Method method = testClass.getMethod("testMethod"); // 파라미터 없는 메서드
Method method = testClass.getMethod("testMethod", null); // null을 줘도 됨
Method method = testClass.getMethod("testMethod", String.class); // 파라미터 1개
Method method = testClass.getMethod("testMethod", new Class[]{String.class, Integer.class}); // 2개 이상
생성자와 동일하게 인자에 맞는 메서드가 없을 경우 NoSuchMethodException이 발생합니다.
리플렉션을 통해 User 클래스의 toString 메서드를 실행해보겠습니다.
사용되는 메서드는 Method 클래스의 invoke(Object obj, Object... args) 입니다.
obj에 들어갈 인자는 해당 메서드를 실행할 클래스의 인스턴스이며, args는 해당 메서드 실행에 필요한 파라미터입니다.
메서드는 객체를 생성하고 그 인스턴스의 참조변수를 통해 실행하기 때문에 인스턴스가 전달되어야 하는 것입니다.
static 메서드일 경우 obj에는 null이 들어갑니다.
public static void main(String[] args) throws Exception{ Class userClass = User.class; Constructor cs = userClass.getConstructor(new Class[]{String.class, String.class, String.class, Integer.class}); User user = (User)cs.newInstance("joontID","joontPWD","joont",920000); Method method = userClass.getMethod("toString"); System.out.println(method.invoke(user)); }
파라미터가 없으므로 getMethod와 invoke 메서드의 뒷쪽 인자는 생략되었습니다.
결과가 잘 출력됨을 보실 수 있습니다.
invoke 메서드의 반환형은 Object입니다. invoke가 호출하는 대상 메서드의 반환형이 반환됩니다.
반환형이 void인 메서드를 호출했을 경우는 NULL이 반환 됩니다 !
앞서 언급했듯이 getMethod 또한 public 접근 제어자만 얻어옵니다.
private 메서드를 실행하실려면 위의 private 객체 생성 처럼 getDeclaredMethod, setAccessible을 사용하시면 됩니다.
예시는 첨부하지 않겠습니다 ~!
감사합니다.
'JAVA,JSP' 카테고리의 다른 글
enum (0) | 2017.02.16 |
---|---|
리플렉션(1), Class 클래스 (5) | 2016.07.29 |
예외처리(2) (0) | 2016.07.03 |
예외처리(1) (0) | 2016.06.30 |
java.lang.NoSuchMethodError (1) | 2016.06.28 |