본문 바로가기
(2024-10) 스파르타 내일배움캠프 - 백엔드/Java Reflect

Getter 동적 생성

by 어뫄어뫄 2024. 11. 27.

TestClassValidator.java

package validation;

import entities.Item;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class TestClassValidator {

    public static void main(String[] args) {
        validateGetters(Item.class);
    }

    public static void validateGetters(Class<?> targetClass) {
        Field[] fields = targetClass.getDeclaredFields();

        Map<String, Method> methodMapping = mapMethodsByName(targetClass);

        for (Field field : fields) {
            String expectedGetterName = "get" + capitalizeFirst(field.getName());

            if (!methodMapping.containsKey(expectedGetterName)) {
                throw new IllegalStateException(
                        String.format("Field '%s' does not have a corresponding getter method", field.getName()));
            }

            Method getterMethod = methodMapping.get(expectedGetterName);

            if (!getterMethod.getReturnType().equals(field.getType())) {
                throw new IllegalStateException(
                        String.format("Getter '%s()' has return type '%s' but expected '%s'",
                                getterMethod.getName(),
                                getterMethod.getReturnType().getTypeName(),
                                field.getType().getTypeName()));
            }

            if (getterMethod.getParameterCount() > 0) {
                throw new IllegalStateException(
                        String.format("Getter '%s' should not accept any parameters but has %d arguments",
                                expectedGetterName, getterMethod.getParameterCount()));
            }
        }
    }

    private static String capitalizeFirst(String input) {
        return input.substring(0, 1).toUpperCase() + input.substring(1);
    }

    private static Map<String, Method> mapMethodsByName(Class<?> targetClass) {
        Method[] methods = targetClass.getMethods();
        Map<String, Method> nameToMethodMap = new HashMap<>();

        for (Method method : methods) {
            nameToMethodMap.put(method.getName(), method);
        }

        return nameToMethodMap;
    }
}

 

entities/Item.java

package entities;

import java.util.Date;

public class Item {
    private double cost;
    private String title;
    private long stock;
    private Date expiryDate;

    // Getters
    public double getCost() {
        return cost;
    }

    public String getTitle() {
        return title;
    }

    public long getStock() {
        return stock;
    }

    public Date getExpiryDate() {
        return expiryDate;
    }
}

 

 

1. Class.getDeclaredFields()

  • 설명: 클래스에 선언된 모든 필드(직렬화된 private 필드 포함)를 반환합니다.
  • 사용 예:
     
    Field[] fields = targetClass.getDeclaredFields();
    • Item 클래스의 필드를 반복적으로 탐색해 Getter 메서드 검증을 수행합니다.

2. Class.getMethods()

  • 설명: 클래스 및 상위 클래스에서 상속받은 모든 public 메서드를 반환합니다.
  • 사용 예:
    Method[] methods = targetClass.getMethods();
    • 메서드 이름과 매핑하여 Map<String, Method>로 관리합니다.

3. Field.getName()

  • 설명: 필드 이름을 반환합니다.
  • 사용 예:
    String expectedGetterName = "get" + capitalizeFirst(field.getName());
     
    • 필드 이름을 기반으로 예상 Getter 메서드 이름을 생성합니다. 예: cost → getCost.

4. Method.getName()

  • 설명: 메서드 이름을 반환합니다.
  • 사용 예:
    nameToMethodMap.put(method.getName(), method);
     
    • 메서드 이름과 Method 객체를 매핑해 빠르게 검색할 수 있도록 처리합니다.

5. Method.getReturnType()

  • 설명: 메서드의 반환 타입을 반환합니다.
  • 사용 예:
     
    if (!getterMethod.getReturnType().equals(field.getType())) { throw new IllegalStateException(...); }
    • 필드 타입과 Getter 메서드의 반환 타입이 일치하는지 확인합니다.

6. Method.getParameterCount()

  • 설명: 메서드의 파라미터 개수를 반환합니다.
  • 사용 예:
    if (getterMethod.getParameterCount() > 0) { throw new IllegalStateException(...); }
    • Getter 메서드가 파라미터를 가지지 않아야 함을 검증합니다.

 

 

검증 과정

1. 클래스 필드 추출

Field[] fields = targetClass.getDeclaredFields();
 

Item 클래스의 필드인 cost, title, stock, expiryDate를 가져옵니다.

2. 메서드 매핑

Map<String, Method> methodMapping = mapMethodsByName(targetClass);
 

Item 클래스의 모든 public 메서드를 Map에 저장합니다.

3. Getter 검증

각 필드에 대해:

  1. 예상 Getter 이름 생성 (get<FieldName>).
  2. Getter 메서드 존재 여부 확인.
  3. 반환 타입과 필드 타입 일치 여부 확인.
  4. 파라미터 개수가 0인지 확인.