코드 컨벤션
은 따로 존재하지 않음 🙅DTO
를 Entity
로 옮기는 로직을 리팩토링 💦여러명
이 여러 DTO 에 대해 작업id
라는 필드를 가지고 있음따라서 추상화되지 않은 클래스
들로부터 같은 타입
, 같은 필드명
을 가진 필드로부터 값을 추출해내기 위해 리플렉션
을 사용하게 되었다.
JDK 8
class BaseEntity {
private Long id;
public BaseEntity(Long id) {
this.id = id;
}
}
class User extends BaseEntity {
private String name;
private String email;
public User(Long id, String name, String email) {
super(id);
this.name = name;
this.email = email;
}
}
public static void main(String[] args) {
User vitamax = new User(1L, "Vitamax", "vitamax@foo.com");
try {
Field field = vitamax.getClass().getDeclaredField("id");
field.setAccessible(true);
System.out.println(field.get(vitamax));
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
결과는 ?
java.lang.NoSuchFieldException: id
at java.lang.Class.getDeclaredField(Class.java:2070)
at com.daehan.test.ReflectionApi.main(ReflectionApi.java:14)
결과는 위와 같이 id 라는 필드값이 없다는 익셉션이 발생하였다.
이유는 간단하다. 말 그대로 id
라는 필드는 User
에 속한 것이 아닌 상위 클래스
BaseEntity
에 있기 때문.
해결방안은 해당 클래스부터 모든 superClass 를 순회하고 해당 필드를 가져오는 것이다.
public static <T> List<Field> getAllFields(T t){
Objects.requireNonNull(t);
Class<?> clazz = t.getClass();
List<Field> fields = new ArrayList<>();
while(clazz != null){ // 1. 상위 클래스가 null 이 아닐때까지 모든 필드를 list 에 담는다.
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
clazz = clazz.getSuperclass();
}
return fields;
}
public static <T> Field getFieldByName(T t, String fieldName){
Objects.requireNonNull(t);
Field field = null;
for(Field f : getAllFields(t)){
if (f.getName().equals(fieldName)){
field = f; // 2. 모든 필드들로부터 fieldName이 일치하는 필드 추출
break;
}
}
if (field != null){
field.setAccessible(true); // 3. 접근 제어자가 private 일 경우
}
return field;
}
public static <T> T getFieldValue(Object obj, String fieldName){
Objects.requireNonNull(obj);
try {
Field field = getFieldByName(obj, fieldName); // 4. 해당 필드 조회 후
return (T) field.get(obj); // 5. get 을 이용하여 field value 획득
} catch (IllegalAccessException e){
return null;
}
}
public static void main(String[] args) {
User vitamax = new User(53L, "Vitamax", "vitamax@foo.com");
Long userId = getFieldValue(vitamax, "id");
System.out.println("userId = " + userId);
}
결과는 ?
userId = 53
위와같이 상위 클래스들의 모든 필드들을 조회한 뒤 필드명을 검색하니 정상적으로 값을 가져올 수 있었다.
위에서 다뤘던 것처럼 동일한 필드명을 갖는 경우에 대해, 리플렉션을 이용하여 상위 클래스들도 조회 후 가져올 수 있었다. 하지만 이는 애초에 동일한 클래스를 상속받지 않고 처리했기 때문에 나타난 결과이다. 따라서 가급적 처음부터 dto 등의 클래스 설계를 잘 하고 간다면 굳이 리플렉션이라는 불필요한 작업을 하지 않아도 된다.
추가적으로 하지만 같은 클래스 & 같은 필드에 대해 똑같은 작업이 반복될 경우 Dynamic Programming 을 이용한다면 굳이 while 문을 돌면서 리소스를 낭비할 필요는 없어보인다. 이는 추후에 시간이 되면 수정해 봐야겠다.
고생하셨네요!