본문 바로가기
Study

[JAVA] 리플렉션(Reflection)이란?

by soeundid 2024. 6. 26.

 

 

java.lang.reflect (Java Platform SE 8 )

Interface Summary  Interface Description AnnotatedArrayType AnnotatedArrayType represents the potentially annotated use of an array type, whose component type may itself represent the annotated use of a type. AnnotatedElement Represents an annotated eleme

docs.oracle.com

 

리플렉션이란?

- 구체적인 클래스 타입을 알지 못하더라도 그 클래스의 메서드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API를 말한다.
- 힙 영역에 로드된 Class 타입의 객체를 통해 클래스의 인스턴스를 생성할 수 있도록 지원하고, 인스턴스 필드와 메서드를 접근 제어자와 상관없이 사용할 수 있도록 지원한다.

 

리플렉션을 사용하는 경우

  • 동적으로 클래스를 사용해야할 때
  • 작성 시점에는 어떠한 클래스를 사용해야 할지 모르지만 런타임 시점에 가져와 실행해야 하는 경우

리플렉션으로 가져올 수 있는 정보

  • Class
  • Constructor
  • Method
  • Field

이제 reflection을 사용해보자!

 

Class 객체 획득 방법

  • java.lang 패키지에서 제공
  • Class 타입에 대한 instance는 Singleton이다
  • 3가지 방법이 있다.
    //방법 1
    Worker worker = new Worker("test", Worker.POSITION_MANAGER);    //일반적인 방식
    Class cls1 = worker.getClass();    

    Class cls2 = Worker.class; //방법 2

    //방법 3
    Class cls3 = null;
    try {
        cls3 = Class.forName("oop21.reflection01.Worker");    //패키지 이름 적으면 됨
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
System.out.println("cls1.hashCode : " + cls1.hashCode());
System.out.println("cls2.hashCode : " + cls2.hashCode());
System.out.println("cls3.hashCode : " + cls3.hashCode());

실행결과

실행결과를 보면 전부 다 같은 해시코드를 가지고 있는 것을 확인할 수 있다.

 

Constructor, Method, Field의 정보 가져오기

가져올 수 있는 방법에는 메서드를 getDeclaredXXX(), getXXX() 사용하는 방법이 있다.

 

1. 먼저 테스트할 클래스 Worker를 만든다.

public class Worker {
    public static final int POSITION_MANAGER = 0;
    public static final int POSITION_ASSISTANT = 1;
    public static final int POSITION_EMPLOYEE = 2;

    private String name;
    private int position;

    public Worker() {
        name = null;
        position = Integer.MAX_VALUE;
    }

    public Worker(String name, int position) {
        super();
        this.name = name;
        this.position = position;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getPosition() {
        return position;
    }
    public void setPosition(int position) {
        this.position = position;
    }

    @Override
    public String toString() {
        return "Worker [name=" + name + ", position=" + position + "]";
    }
}

 

2. getDeclaredXXX() 메서드를 사용하는 방법

public class ReflectionTest2 {
    public static void main(String[] args) {
        Class<?> cls = Worker.class;

        // Constructor 타입의 객체를 배열로 얻어옴
        // 지금은 생성자 2개 구현되어 있으니 2개를 배열로 얻어옴
        Constructor<?>[] constructors = cls.getDeclaredConstructors();
        System.out.println("=================== [생성자] ===================");
        for(int i = 0; i<constructors.length; i++) {
            System.out.println(constructors[i].toString());
        }

        System.out.println();

        //method 정보 가져오기
        Method[] methods = cls.getDeclaredMethods();
        System.out.println("=================== [메서드] ===================");
        for(int i = 0; i<methods.length; i++) {
            System.out.println(methods[i].toString());
        }

        System.out.println();

        //field 정보 가져오기
        Field[] fields = cls.getDeclaredFields();
        System.out.println("=================== [필드] ===================");
        for(int i = 0; i<fields.length; i++) {
            System.out.println(fields[i].toString());
        }
    }
}

getDeclaredXXX() 사용한 결과

 

결과를 보면 Worker 클래스에 정의한 생성자 2개, 메소드 5개, 필드 5개가 나오는 것을 확인할 수 있다.

 

3. getXXX() 메서드를 사용하는 방법

public class ReflectionTest3 {
    public static void main(String[] args) {
        Class<?> cls = Worker.class;

        Constructor<?>[] constructors = cls.getConstructors();
        System.out.println("=================== [생성자] ===================");
        for(int i = 0; i<constructors.length; i++) {
            System.out.println(constructors[i].toString());
        }

        System.out.println();

        //method 정보 가져오기
        Method[] methods = cls.getMethods();
        System.out.println("=================== [메서드] ===================");
        for(int i = 0; i<methods.length; i++) {
            System.out.println(methods[i].toString());
        }

        System.out.println();

        //field 정보 가져오기
        Field[] fields = cls.getFields();
        System.out.println("=================== [필드] ===================");
        for(int i = 0; i<fields.length; i++) {
            System.out.println(fields[i].toString());
        }
    }
}

getXXX() 사용한 결과

getXXX() 메서드를 사용한 결과를 보면

메서드는 우리가 Worker 클래스에 정의한 method 이외에 Object 클래스로부터 상속받은 메서드 정보도 같이 나오는 것을 확인할 수 있다.

필드는 private으로 선언한 name과 position의 정보는 가져오지 못하는 것을 확인할 수 있다.