Niestandardowy program do mapowania klas w Javie
W tym artykule wyjaśnię, jak zaimplementować niestandardową klasę użytkową mappera w Javie bez korzystania z zewnętrznych bibliotek, takich jak MapStruct.
Przede wszystkim powinniście mieć podstawową wiedzę na temat języka programowania Java i Java Reflection API.
Zaczynajmy.
public static <O, Target> O map(Object instance, Class<Target> targetClass) {
if (instance.getClass().isPrimitive() || targetClass.isPrimitive()) {
throw new RuntimeException("Provide a non primitive class");
}
if (instance.getClass().isInterface() || targetClass.isInterface()) {
throw new RuntimeException("Provide a non interface class");
}
if (instance.getClass().isEnum() || targetClass.isEnum()) {
throw new RuntimeException("Provide a non enum class");
}
if (Modifier.isAbstract(instance.getClass().getModifiers()) || Modifier.isAbstract(targetClass.getModifiers())) {
throw new RuntimeException("Provide a non abstract class");
}
return doMap(instance, instance.getClass(), targetClass);
}
@SneakyThrows
private static <O, Source, Target> O doMap(Object sourceInstance, Class<Source> sourceClass, Class<Target> targetClass) {
Set<Field> declaredSourceFields = findAllFields(sourceClass);
Set<Field> declaredTargetFields = findAllFields(targetClass);
Object targetInstance = targetClass.getDeclaredConstructor().newInstance();
for (Field field : declaredTargetFields) {
Field sourceField = findSourceField(field, declaredSourceFields);
if (sourceField != null) {
field.setAccessible(true);
field.set(targetInstance, sourceField.get(sourceInstance));
}
}
return (O) targetInstance;
}
public static Set<Field> findAllFields(Class<?> inputClass) {
Set<Field> allFields = new HashSet<>();
if (inputClass == null) {
return allFields;
}
Field[] fields = inputClass.getDeclaredFields();
allFields.addAll(Set.of(fields));
allFields.addAll(findAllFields(inputClass.getSuperclass()));
return allFields;
}
private static Field findSourceField(Field targetField, Set<Field> declaredSourceFields) {
for (Field field : declaredSourceFields) {
field.setAccessible(true);
String sourceFieldName = field.getName();
if (sourceFieldName.equals(targetField.getName()) &&
field.getType().equals(targetField.getType())) {
return field;
}
}
return null;
}
Przykłady:
@Setter
@Getter
public class SourceClass {
String surname;
Integer age;
private String name;
@Override
public String toString() {
return "SourceClass{" +
"surname='" + surname + '\'' +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
@Setter
@Getter
public class TargetClass {
Integer age;
private String name;
@Override
public String toString() {
return "TargetClass{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
public static void main(String[] args) {
SourceClass sourceInstance = new SourceClass();
sourceInstance.setAge(27);
sourceInstance.setName("Java");
sourceInstance.setSurname("Reflection");
TargetClass targetInstance = CustomMapper.map(sourceInstance, TargetClass.class);
System.out.println(targetInstance);
}
Nazwisko nie istnieje w danych wyjściowych, ponieważ nie jest dostępne w klasie docelowej. Zatem metoda map ignoruje to pole.
Wniosek:
Większość bibliotek, frameworków i idei napisanych w Javie wykorzystuje Java Reflection API. Ale ten interfejs API jest trochę powolny, ponieważ sprawdza uprawnienia bezpieczeństwa dla każdej operacji. Aby uzyskać lepszą wydajność, możesz sprawdzić uchwyty metody Java z tego łączahttps://docs.oracle.com/javase/8/docs/api/java/lang/invoke/MethodHandles.html.
Dziękuję za przeczytanie.
Z wyrazami szacunku

![Czym w ogóle jest lista połączona? [Część 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































