Field.set(instance, value)
- 해당 인스턴스 field에 value값을 할당할 수 있다.
- 필드 유형에 유효한 값이 아닐 시에는 예외가 발생한다.
- 제한된 접근 제어자일 시에는 setAccessible 메소드에 true 값을 줘야한다.
필드 조정 사용 사례
- JSON 역 직렬화
- ORM 매핑
- 구성 파일 파서
필드 설정 주의 사항
- final이 붙은 필드의 경우 reflection을 통한 설정은 권장되지 않고 예상치 못한 결과가 나올 수 있으므로 주의가 필요하다.
- 클래스에서 final이 붙은 필드는 컴파일러가 최적화하여 해당 필드가 쓰인 메소드에서 해당 값으로, 상수로 치환한다. 따라서 reflection으로 변경한다 해도 해당 필드는 더이상 참조하지 않으므로 설정된 필드값이 아닌 기존의 final로 설정한 값이 출력된다.
- 정적 final 필드로 설정하면 읽기만 가능한 필드로 설정되기에 setAccessible(true)로 설정해도 값을 변경하려고 할 시에 예외가 발생한다.
Array.newInstance(Class<?> componentType, int length) : componentType을 타입으로 가지는 1차원 배열을 생성한다.
Array.set(Object arrayObject, int index, Object value)
- arrayObject의 index 번째 요소의 값을 value로 설정한다. 이 때 value의 타입이 유효하지 않은 경우 예외가 발생한다.
- 원시 유형에 컴파일 타임 보안을 원하면 아래와 같이 사용할 수 있다.
- Array.setBoolean(Object arrayObject, int index, boolean value)
- Array.setInt(Object arrayObject, int index, int value)
- Array.setLong(Object arrayObject, int index, long value)
- Array.setDouble(Object arrayObject, int index, double value)
- ...
구성 파일 파서 Example
public class Main {
private static final Path GAME_CONFIG_PATH = Path.of("resources/game-properties.cfg");
private static final Path UI_CONFIG_PATH = Path.of("resources/user-interface.cfg");
public static void main(String[] args) throws IOException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
// GameConfig config = createConfigObject(GameConfig.class, GAME_CONFIG_PATH);
UserInterfaceConfig config = createConfigObject(UserInterfaceConfig.class, UI_CONFIG_PATH);
System.out.println(config);
}
public static <T> T createConfigObject(Class<T> clazz, Path filePath) throws IOException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Scanner scanner = new Scanner(filePath);
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
T configInstance = (T) constructor.newInstance();
while (scanner.hasNextLine()) {
String configLine = scanner.nextLine();
String[] nameValuePair = configLine.split("=");
String propertyName = nameValuePair[0];
String propertyValue = nameValuePair[1];
Field field;
try {
field = clazz.getDeclaredField(propertyName);
} catch (NoSuchFieldException e) {
System.out.printf("Property name : %s is unsupported\n", propertyName);
continue;
}
field.setAccessible(true);
Object parsedValue;
if(field.getType().isArray()) {
parsedValue = parseArray(field.getType().componentType(), propertyValue);
}else {
parsedValue = parseValue(field.getType(), propertyValue);
}
field.set(configInstance, parsedValue);
}
return configInstance;
}
private static Object parseArray(Class<?> arrayElementType, String value) {
String[] elementValues = value.split(",");
Object arrayObject = Array.newInstance(arrayElementType, elementValues.length);
for (int i = 0; i < elementValues.length; i++) {
Array.set(arrayObject, i, parseValue(arrayElementType, elementValues[i]));
}
return arrayObject;
}
private static Object parseValue(Class<?> type, String value) {
if (type.equals(int.class)) {
return Integer.parseInt(value);
} else if (type.equals(short.class)) {
return Short.parseShort(value);
} else if (type.equals(long.class)) {
return Long.parseLong(value);
} else if (type.equals(double.class)) {
return Double.parseDouble(value);
} else if (type.equals(float.class)) {
return Float.parseFloat(value);
} else if (type.equals(String.class)) {
return value;
}
throw new RuntimeException(String.format("Type : %s unsupported", type.getTypeName()));
}
}
cf)Array.newInstance()가 Object를 반환하긴 하지만 내부적으로 배열 형태이기 때문에 (int)와 같이 형변환을 진행하면 int[] 형태가 된다.
'JAVA > Reflection' 카테고리의 다른 글
자바 제어자 with Reflection (0) | 2024.11.27 |
---|---|
메서드 탐색과 호출 with Reflection (0) | 2024.11.26 |
필드 검사와 배열 검사 with Reflection (0) | 2024.11.24 |
객체 생성과 생성자 with Reflection (0) | 2024.11.23 |
Reflection 개요 (0) | 2024.11.23 |