Пользовательский преобразователь классов в Java

Feb 16 2023
В этой статье я объясню, как реализовать собственный класс утилиты сопоставления на Java без использования сторонних библиотек, таких как MapStruct. Прежде всего, ребята, у вас должны быть базовые знания языка программирования Java и Java Reflection API.

В этой статье я объясню, как реализовать собственный класс утилиты сопоставления на Java без использования сторонних библиотек, таких как MapStruct.

Прежде всего, ребята, у вас должны быть базовые знания языка программирования Java и Java Reflection API.

Давайте начнем.

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;
}

Примеры:

@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);
}

Фамилия не существует в выходных данных, потому что она недоступна в целевом классе. Таким образом, метод карты игнорирует это поле.

Вывод:
большинство библиотек, фреймворков и IDE, написанных на Java, используют Java Reflection API. Но этот API немного медленный, потому что он проверяет разрешения безопасности для каждой операции. Для повышения производительности вы можете проверить дескрипторы методов Java по этой ссылке.https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/MethodHandles.html.

Спасибо за чтение.
С наилучшими пожеланиями