더 자바 리플렉션(2)
728x90

인프런에 있는 백기선님의 더 자바, 코드를 조작하는 다양한 방법 을 보고 정리하는 글입니다.

리플렉션을 이용한 클래스 정보 수정 및 실행

전 글에서 정리했던 클래스 정보를 불러오는 것을 이용해서 클래스 정보 수정을 해보겠다.

 

일단 다양한 접근제어자를 가진 필드와 메소드들을 만든다.

 

Book.java

public class Book {

    public static String A = "A";

    private String B = "B";

    public Book() {

    }

    public Book(String b){
        B = b;
    }

    public void c() {
        System.out.println("C");
    }
    public int sum(int left,int right) {
        return left+right;
    }

}

이제 이렇게 만든 클래스를 이용해서 다른 클래스에서 인스턴스를 생성한다.

 

App.java

public class App {

    public static void main( String[] args ) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {

        Class<?> bookClass  = Class.forName("me.whiteship.Book");
        //인스턴스를 만드는 가장 권장하는 방법 생성자를 쓰는 것
        Constructor<?> constructor = bookClass.getConstructor(null);
        Book book = (Book)constructor.newInstance("myBook");
        System.out.println(book);
	}
}

예전에는 getInstance(); 를 사용해서 인스턴스를 생성 했지만 지금은 사용할 수 없게 되었다.

 

이 강의에서는 생성자를 가져와서 인스턴스를 생성하는 것을 권장한다고 했다.

 

처음에 Book 클래스에는 Default 생성자와 String을 파라미터로 받는 생성자 2개가 있었다. 위의 코드는 Default 생성자를 받아와서 보여주는 코드이다.

 

만약 생성자가 파라미터를 받는다면 getConstructor 부분에 파라미터 타입을 명시해주면 된다.

 Class<?> bookClass  = Class.forName("me.whiteship.Book");
 Constructor<?> constructor = bookClass.getConstructor(String.class); //생성자가 파라미터를 받을 때
 Book book = (Book)constructor.newInstance("myBook");
 System.out.println(book);

 

이제 인스턴스를 가져오는 방법을 알았다. 인스턴스 외에 필드 값과 메소드들을 가져와서 수정할 때는 이렇게 해주면 된다.

 

 Field a = Book.class.getDeclaredField("A");
  
 System.out.println(a.get(null));
 
 a.set(null,"BBBBB");
 
 System.out.println(a.get(null));

실행 결과

String A 는 static 이므로 특정 인스턴스에 속해 있지 않아 a.get() 부분에 null 을 넣어 줬다.

 

하지만 특정 인스턴스에 속해있는 것은 어떻게 할까?

 

먼저 Book 클래스에 있는 String B를 대상으로 해보겠다.

 Field b = Book.class.getDeclaredField("B");
 //특정 인스턴스에 속해 있으므로 아까처럼 null로 가져올 수 없다.
  b.setAccessible(true); // B는 private 이므로 setAccessible 을 true로 하면 가져올 수 있다.
  System.out.println(b.get(book));
  b.set(book, "BBBBBBBB");
  System.out.println(b.get(book));

setAccessible(true); 는 이제 String B가 private 이므로 접근을 할 수 없기 때문에 접근을 할 수 있도록 setAccessible(true);로 해준다.

 

실행 결과

myBook 이 나온 이유는 Book 인스턴스를 가져올 때 생성자에 myBook을 파라미터로 넣어줬기 때문이다.

 

Book 클래스의 생성자 중에서 파라미터를 받는 생성자는

Book.java

  public Book(String b){
        B = b;
  }

이것이므로 Field에서 B를 가져올 때 처음 인스턴스 생성시에 수정된 myBook이 들어가 있는 것이다.

 

이제 메소드 c 와 sum을 가져 올 차례이다.

 

Method도 위의 과정과 비슷하다.

 

Method c()

  Method c = Book.class.getDeclaredMethod("c");
        c.setAccessible(true);// private method 이므로
        c.invoke(book);

마찬가지로 invoke 하는 메소드가 특정 인스턴스에 속해있는 메소드라면 인스턴스를 파라미터로 넘겨줘야 한다.

c()는 파라미터로 아무것도 받지 않으므로 book 인스턴스만 넘겨 주면 호출이 된다.

그리고 접근제어자가 private로 되어 있으므로 setAccessible(true) 로 해놓고 실행해야된다. 

 

실행 결과

 

Method sum()

// public 이고 파라미터를 받는 메소드라면        프리미티어 타입이랑 레퍼런스 타입 구분함....
Method d = Book.class.getDeclaredMethod("sum", int.class, int.class);
int invoke = (int) d.invoke(book,1,2);
System.out.println(invoke);

sum 은 파라미터를 받는 메서드 이므로 getDeclaredMethod에다가 파라미터 타입을 넣어준다.

 

그리고 invoke 시 특정 인스턴스에 속해 있으므로 인스턴스를 넘겨주고 파라미터 값도 넘겨준다.

 

실행 결과

 

728x90

'더 자바' 카테고리의 다른 글

더자바 리플렉션 (1)  (0) 2022.04.01
더 자바 바이트코드 조작  (0) 2022.03.31
더 자바 JVM  (0) 2022.03.31