… оказывается, мне это было не нужно!
Прошло некоторое время, когда я хотел попробовать что-нибудь с дженериками. Возможность просто поразила меня, когда мне показали реализацию с отражением. Это две темы, с которыми я хочу чувствовать себя более комфортно, так что … идеальная возможность!
Возможность
Я реализовывал HTTP-запрос ИСПРАВЛЕНИЯ на контроллере для объекта под названием Restaurant в рамках курса REST API, который я прохожу. Реализация, которую придумал инструктор, была примерно такой (с использованием отражения):
@PatchMapping("/{id}") public ResponseEntity> partialUpdate(@PathVariable Long id, @RequestBody MaprestaurantMap) { 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 MaprestaurantMap) { 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(MapobjectMap, 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”