Рубрики
Без рубрики

Моя первая попытка использовать дженерики…

… оказывается, мне это было не нужно! Прошло некоторое время, когда я хотел попробовать что-нибудь с дженериками. T… С тегами java, generics, reflection, spring.

… оказывается, мне это было не нужно!

Прошло некоторое время, когда я хотел попробовать что-нибудь с дженериками. Возможность просто поразила меня, когда мне показали реализацию с отражением. Это две темы, с которыми я хочу чувствовать себя более комфортно, так что … идеальная возможность!

Возможность

Я реализовывал HTTP-запрос ИСПРАВЛЕНИЯ на контроллере для объекта под названием Restaurant в рамках курса REST API, который я прохожу. Реализация, которую придумал инструктор, была примерно такой (с использованием отражения):

@PatchMapping("/{id}")
public ResponseEntity partialUpdate(@PathVariable Long id, @RequestBody Map restaurantMap) {
    Restaurant restaurantToUpdate = restaurantCrudService.read(id);

    if (restaurantToUpdate == null) {
        return ResponseEntity.notFound().build();
    }

    merge(restaurantMap, restaurantToUpdate);

    return update(id, restaurantToUpdate);
}

public merge(Map objectMap, Restaurant restaurantToUpdate) {
    ObjectMapper objectMapper = new OjectMapper()
    Restaurant newObject = objectMapper.convertValue(objectMap, Restaurant.class);

    objectMap.forEach((fieldProp, valueProp) -> {
        Field field = ReflectionUtils.findField(Restaurant.class, fieldProp);
        field.setAccessible(true);

        Object newValue = ReflectionUtils.getField(field, newObject);

        ReflectionUtils.setField(field, objectToUpdate, newValue);
    });
}

И мне пришлось бы переопределить этот метод на всех моих контроллерах (для других объектов). Итак, я попытался создать решение с помощью дженериков.

Решение

Универсальный Класс

Это класс, который представляет приведенный выше код, но с учетом обобщений:

public class ObjectMerger {

    private ObjectMapper objectMapper;
    private Class type;

    private ObjectMerger(Class type) {
        this.objectMapper = new ObjectMapper();
        this.type = type;
    }

    public static ObjectMerger of(Class type) {
        return new ObjectMerger(type);
    }

    public void mergeRequestBodyToGenericObject(Map objectMap, T objectToUpdate) {
        T newObject = objectMapper.convertValue(objectMap, type);

        objectMap.forEach((fieldProp, valueProp) -> {
            Field field = ReflectionUtils.findField(type, fieldProp);
            field.setAccessible(true);

            Object newValue = ReflectionUtils.getField(field, newObject);

            ReflectionUtils.setField(field, objectToUpdate, newValue);
        });
    }
}

Вы можете видеть, что я могу создать экземпляр этого с любым другим типом класса (следовательно, “generic”), используя of метод, и класс предоставит метод для сопоставления Map Object> с предоставленным типом объекта. Object>

Новая реализация для метода PATCH HTTP

Это новая реализация для метода PATCH HTTP любого контроллера с этого момента (здесь, пример ресторана):

@PatchMapping("/{id}")
public ResponseEntity partialUpdate(@PathVariable Long id, @RequestBody Map restaurantMap) {
    Restaurant restaurantToUpdate = restaurantCrudService.read(id);

    if (restaurantToUpdate == null) {
        return ResponseEntity.notFound().build();
    }

    ObjectMerger
        .of(Restaurant.class)
        .mergeRequestBodyToGenericObject(restaurantMap, restaurantToUpdate);

    return update(id, restaurantToUpdate);
}

Ждать… Я был неправ!

То, что я хотел сделать, было просто СУХИМ.

Не повторяйтесь , то есть решение, в котором я бы не стал повторяться для HTTP-запросов PATCH (в данном случае). Я думал, что дженерики – это решение, потому что… причины! Ну, на данный момент я точно не знаю. Все, что я сейчас помню, это то, что я также имел в виду, что не хотел создавать экземпляры объектов с помощью new .

Итак, вот она, для исторических целей, реализация дженериков: та, о которой я думал своей задницей .

И мне даже не нужна была настройка.

public static ObjectMerger of(Class type) {
    return new ObjectMerger(type);
}

Итак, я создал метод of для возврата нового ObjectMerger: тот, в котором кто-то помог мне немного разобраться .

И чтобы избежать создания экземпляра, я мог бы кэшировать его на карте.

public static ObjectMerger of(Class type) {

    if (!cacheEnabled) {
        // Cache is not enabled. A new instance is always created.
        return new ObjectMerger(type);
    }

    if (!objectMergerCache.containsKey(type)) {
        ObjectMerger objectMerger = new ObjectMerger(type);
        objectMergerCache.put(type, objectMerger);
        // Cache enabled. Instance created (first request).
        return objectMerger;
    }

    // Cache enabled. Returning existing instance.
    return objectMergerCache.get(type);
}

Эта идея привела к такой реализации: та, где я был почти там .

Ну, а что я вообще кэширую?

public class ObjectMerger {

    public static void mergeRequestBodyToGenericObject(Map objectMap, Object objectToUpdate, Class type) {
        ObjectMapper objectMapper = new ObjectMapper();
        Object newObject = objectMapper.convertValue(objectMap, type);

        objectMap.forEach((fieldProp, valueProp) -> {
            Field field = ReflectionUtils.findField(type, fieldProp);
            field.setAccessible(true);

            Object newValue = ReflectionUtils.getField(field, newObject);

            ReflectionUtils.setField(field, objectToUpdate, newValue);
        });
    }
}

Мне не нужны были какие-либо свойства класса, это мог быть вспомогательный класс, без состояния, просто функция. Поэтому я создал очень простой статический метод: тот, в котором я чувствовал себя идиотом .

И это все

Это работает, я многому научился и я очень доволен собой. Хахахахаха.

Вы можете ознакомиться с историей этого поста здесь . Вы можете протестировать API здесь .

Изображение U3100481

Оригинал: “https://dev.to/brunodrugowick/my-first-attempt-at-generics-976”