Mappatore di classi personalizzato in Java
In questo articolo, spiegherò come implementare una classe util mapper personalizzata in Java senza utilizzare librerie di terze parti come MapStruct.
Prima di tutto, ragazzi, dovreste avere una conoscenza di base del linguaggio di programmazione Java e dell'API Java Reflection.
Iniziamo.
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;
}
Esempi:
@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);
}
Il cognome non esiste nell'output perché non è disponibile nella classe di destinazione. Quindi il metodo map ignora questo campo.
Conclusione:
la maggior parte delle librerie, dei framework e degli idi scritti in Java sfrutta l'API Java Reflection. Ma questa API è un po' lenta perché controlla le autorizzazioni di sicurezza per ogni operazione. Per prestazioni migliori puoi controllare gli handle del metodo Java da questo linkhttps://docs.oracle.com/javase/8/docs/api/java/lang/invoke/MethodHandles.html.
Grazie per aver letto.
Distinti saluti

![Che cos'è un elenco collegato, comunque? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































