CodeGym /행동 /JAVA 25 SELF /클래스, 필드, 메서드 정보 가져오기

클래스, 필드, 메서드 정보 가져오기

JAVA 25 SELF
레벨 62 , 레슨 1
사용 가능

1. 필드 정보 가져오기

getFields()getDeclaredFields()의 차이

Java에서 각 클래스는 클래스 내부에 선언된 변수인 필드(fields)를 가집니다. 리플렉션을 사용하면 필드 이름, 타입, 접근 제한자(public/private) 등을 알아내고, 런타임에 접근할 수도 있습니다. 진입점은 Class<?> 객체입니다.

Class가 제공하는 필드 관련 핵심 메서드:

메서드 반환 내용
getFields()
클래스와 그 부모 및 인터페이스의 모든 public 필드 배열
getDeclaredFields()
해당 클래스에 선언된 모든 필드 배열

비유하자면: getFields()는 전시 공간(public)만 도는 투어이고, getDeclaredFields()는 기술 구역(private, protected, package-private)까지 포함합니다.

예: 클래스의 모든 필드 출력

다음과 같은 클래스가 있다고 합시다:

public class Person {
    public String name;
    private int age;
    protected String email;
}

필드 정보를 가져봅시다:

import java.lang.reflect.Field;

Class<?> clazz = Person.class;

// 모든 public 필드(상속된 것 포함)
System.out.println("Public fields:");
for (Field field : clazz.getFields()) {
    System.out.println(field.getName() + " : " + field.getType().getSimpleName());
}

// 선언된 모든 필드(private 포함, 자신의 것만)
System.out.println("\nDeclared fields:");
for (Field field : clazz.getDeclaredFields()) {
    System.out.println(field.getName() + " : " + field.getType().getSimpleName());
}

결과:

Public fields:
name : String

Declared fields:
name : String
age : int
email : String

필드의 접근 제한자/수정자는 어떻게 확인할까?

각 필드(Field)는 접근 제한자(public/private/protected, static, final 등)를 가집니다. getModifiers()로 값을 얻은 뒤 Modifier를 사용해 문자열로 변환할 수 있습니다:

import java.lang.reflect.Modifier;

for (Field field : clazz.getDeclaredFields()) {
    int mods = field.getModifiers();
    System.out.println(field.getName() + " : " + Modifier.toString(mods));
}

2. 메서드 정보 가져오기

getMethods()getDeclaredMethods()

메서드는 객체가 수행할 수 있는 동작입니다. 리플렉션을 사용하면 클래스에 어떤 메서드가 있는지, 그 파라미터, 반환 타입, 한정자, 애너테이션 등을 확인할 수 있습니다.

메서드 반환 내용
getMethods()
클래스와 부모의 모든 public 메서드(Object 포함)
getDeclaredMethods()
해당 클래스에 선언된 모든 메서드(private 포함)

예: 클래스의 모든 메서드 출력

import java.lang.reflect.Method;

System.out.println("Public methods:");
for (Method method : clazz.getMethods()) {
    System.out.println(method.getName());
}

System.out.println("\nDeclared methods:");
for (Method method : clazz.getDeclaredMethods()) {
    System.out.println(method.getName());
}

결과(Person 기준):

Public methods:
getClass
hashCode
equals
toString
notify
notifyAll
wait
wait
wait

Declared methods:
(아무것도 없음, Person에 자체 메서드가 없는 경우)

Person에 메서드를 하나 추가해 봅시다:

public class Person {
    public String name;
    private int age;
    protected String email;

    public void sayHello() {
        System.out.println("Hi!");
    }
}

이제 getDeclaredMethods()가 해당 메서드도 보여줍니다.

메서드의 매개변수와 반환 타입을 확인하는 방법

for (Method method : clazz.getDeclaredMethods()) {
    System.out.print(Modifier.toString(method.getModifiers()) + " ");
    System.out.print(method.getReturnType().getSimpleName() + " ");
    System.out.print(method.getName() + "(");
    Class<?>[] params = method.getParameterTypes();
    for (int i = 0; i < params.length; i++) {
        System.out.print(params[i].getSimpleName());
        if (i < params.length - 1) System.out.print(", ");
    }
    System.out.println(");");
}

출력:

public void sayHello();

3. 생성자 정보 가져오기

생성자는 객체 생성을 위해 사용하는 특별한 메서드입니다.

메서드 반환 내용
getConstructors()
모든 public 생성자
getDeclaredConstructors()
클래스에 선언된 모든 생성자

예: 클래스의 모든 생성자 출력

import java.lang.reflect.Constructor;

System.out.println("Constructors:");
for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
    System.out.print(clazz.getSimpleName() + "(");
    Class<?>[] params = constructor.getParameterTypes();
    for (int i = 0; i < params.length; i++) {
        System.out.print(params[i].getSimpleName());
        if (i < params.length - 1) System.out.print(", ");
    }
    System.out.println(");");
}

클래스에 기본 생성자만 있어도 출력됩니다.

4. 애너테이션: 클래스, 메서드 또는 필드에 무엇이 붙었는지 확인하기

애너테이션은 클래스, 메서드, 필드, 매개변수에 부착할 수 있는 특별한 메타데이터입니다. 리플렉션을 통해 요소에 어떤 애너테이션이 있는지 확인할 수 있습니다.

애너테이션 가져오기

  • 클래스: clazz.getAnnotations()
  • 메서드: method.getAnnotations()
  • 필드: field.getAnnotations()

예: @Deprecated 애너테이션 존재 여부 확인

for (Method method : clazz.getDeclaredMethods()) {
    if (method.isAnnotationPresent(Deprecated.class)) {
        System.out.println(method.getName() + " is @Deprecated");
    }
}

예: 클래스의 모든 애너테이션 출력

for (var annotation : clazz.getAnnotations()) {
    System.out.println(annotation);
}

5. 실습: 클래스 구조 분석 미니 프로그램

지금까지를 종합하여 클래스 이름을 받아 구조(필드, 메서드, 생성자, 애너테이션)를 출력하는 작은 유틸리티를 만들어 봅시다.

코드 예시: ClassInspector

import java.lang.reflect.*;
import java.util.Scanner;

public class ClassInspector {
    public static void main(String[] args) throws Exception {
        Scanner scanner = new Scanner(System.in);
        System.out.print("클래스의 정규 이름을 입력하세요(예: java.util.ArrayList): ");
        String className = scanner.nextLine();

        Class<?> clazz = Class.forName(className);

        System.out.println("\n== 클래스: " + clazz.getName() + " ==");

        // 클래스 애너테이션
        System.out.println("애너테이션:");
        for (Annotation annotation : clazz.getAnnotations()) {
            System.out.println("  " + annotation);
        }

        // 필드
        System.out.println("\n필드:");
        for (Field field : clazz.getDeclaredFields()) {
            System.out.println("  " + Modifier.toString(field.getModifiers())
                    + " " + field.getType().getSimpleName()
                    + " " + field.getName());
        }

        // 메서드
        System.out.println("\n메서드:");
        for (Method method : clazz.getDeclaredMethods()) {
            System.out.print("  " + Modifier.toString(method.getModifiers())
                    + " " + method.getReturnType().getSimpleName()
                    + " " + method.getName() + "(");
            Class<?>[] params = method.getParameterTypes();
            for (int i = 0; i < params.length; i++) {
                System.out.print(params[i].getSimpleName());
                if (i < params.length - 1) System.out.print(", ");
            }
            System.out.println(");");
        }

        // 생성자
        System.out.println("\n생성자:");
        for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
            System.out.print("  " + Modifier.toString(constructor.getModifiers())
                    + " " + clazz.getSimpleName() + "(");
            Class<?>[] params = constructor.getParameterTypes();
            for (int i = 0; i < params.length; i++) {
                System.out.print(params[i].getSimpleName());
                if (i < params.length - 1) System.out.print(", ");
            }
            System.out.println(");");
        }
    }
}

작동 방식

  • 사용자가 클래스의 정규 이름(예: "java.util.ArrayList" 또는 본인 클래스)을 입력합니다.
  • 프로그램이 클래스를 동적으로 로드하여 애너테이션, 필드, 메서드, 생성자를 출력합니다.
  • JDK 표준 클래스들로 시도해 보세요 — 내부에 흥미로운 내용이 얼마나 많은지 볼 수 있습니다!

6. 유용한 포인트와 시각화

시각화: 리플렉션으로 클래스에서 알 수 있는 것들

graph TD
    A[Class<?>] --> B["getFields/getDeclaredFields"]
    A --> C[getMethods/getDeclaredMethods]
    A --> D[getConstructors/getDeclaredConstructors]
    A --> E[getAnnotations]
    B --> F[Field: 타입, 이름, 수정자]
    C --> G[Method: 반환 타입, 매개변수]
    D --> H[Constructor: 매개변수]
    E --> I["Annotation[]"]

표: 어디에서 정보를 찾을까

무엇을 알고 싶은가 리플렉션으로 얻는 방법
모든 public 필드
clazz.getFields()
선언된 모든 필드
clazz.getDeclaredFields()
모든 public 메서드
clazz.getMethods()
선언된 모든 메서드
clazz.getDeclaredMethods()
모든 public 생성자
clazz.getConstructors()
선언된 모든 생성자
clazz.getDeclaredConstructors()
클래스 애너테이션
clazz.getAnnotations()
메서드/필드 애너테이션
method.getAnnotations(), field.getAnnotations()
수정자
Modifier.toString(field.getModifiers())

7. 리플렉션 사용할 때 흔한 실수

오류 №1: getFields()getDeclaredFields()를 혼동.
private 필드를 찾고 있다면 getFields()가 아니라 getDeclaredFields()를 사용하세요. 전자는 클래스에 선언된 모든 필드를 반환하고, 후자는 오직 public 필드(그리고 상속된 필드까지!)만 반환합니다.

오류 №2: Checked 예외를 처리하지 않음.
많은 리플렉션 API가 예외를 던집니다(예: ClassNotFoundException, SecurityException). 반드시 처리하거나 메서드 시그니처에 선언하세요.

오류 №3: 접근 제한자를 고려하지 않음.
private 필드와 메서드에 접근하려면 setAccessible(true)를 호출해야 하며, 그렇지 않으면 IllegalAccessException이 발생합니다. 예:

Field field = clazz.getDeclaredField("age");
field.setAccessible(true); // private 필드에 대한 접근 허용
int value = (int) field.get(person);

오류 №4: 자신이 작성한 멤버만 보일 것으로 기대함.
getMethods()getFields()는 현재 클래스뿐 아니라 모든 부모 클래스(그리고 Object까지)의 public 멤버를 반환합니다. 자신이 작성한 것만 보일 거라 기대했다면 놀랄 수 있습니다.

오류 №5: 애너테이션 존재 여부를 올바르게 확인하지 않음.
특정 애너테이션을 확인할 때는 isAnnotationPresent()를 사용하세요. 불필요하게 애너테이션 배열을 직접 순회하지 마십시오.

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION