Java Generic как привести к подтипу для вызова универсального метода - PullRequest
0 голосов
/ 04 декабря 2018

У меня следующий интерфейс:

public interface EntityCloneService<T extends AbstractNamedEntityBase> {

 /**
  * Given a UUID load the Entity Type.
  * 
  * @param uuid
  * @return  Entity From DB and 
  */
 public T getByUuid(UUID uuid);


 /**
  * Given the Existing Entity,  clone it and save in DB, then return clone instance.
  */
 public T getCloneAndSave(T existingEntity) throws Exception;

}

Теперь у меня есть Generic Service, где у меня есть

@Component
public class GenericEntityCloneService {

    private static final Map<String,EntityCloneService<? extends AbstractNamedEntityBase>> registration = 
            new HashMap<String,EntityCloneService<? extends AbstractNamedEntityBase>>(); // here we have registration of all entity by name to service actual implementation.

    public void clone(AbstractNamedEntityBase existingEntity) {
        EntityCloneService<? extends AbstractNamedEntityBase> service = registration.get("SOME KEY");
        AbstractNamedEntityBase  entity =   service.getByUuid(ref.getUuid());  // THIS WORKS because it up casting.

        service.getCloneAndSave(entity);    // now how do I pass entity object such that 
    }
}

Когда я пытаюсь скомпилировать этот код не компилируется.Я знаю, что getCloneAndSave () я передаю тип AbstractNamedEntityBase, что не допускается.Итак, как мне сделать вызов service.getCloneAndSave (entity);Любая помощь очень ценится.Я использую Java 8.

Ответы [ 2 ]

0 голосов
/ 06 декабря 2018

Проблема в следующем:

AbstractNamedEntityBase entity = service.getByUuid(ref.getUuid());

Эта строка отбрасывает T.Каждый EntityCloneService работает только с одним подклассом AbstractNamedEntityBase.Для метода getCloneAndSave службы требуется объект типа T, который является некоторым конкретным подклассом AbstractNamedEntityBase.

Нет способа сохранить универсальный тип значения в вашей карте.Все, что вы когда-либо узнаете об этом, это то, что это EntityCloneService<? extends AbstractNamedEntityBase>.Однако у вас есть несколько вариантов, чтобы обойти это.

Самый простой - это добавить метод по умолчанию в EntityCloneService.Внутри этого интерфейса T известен, и нет необходимости напрямую ссылаться на AbstractNamedEntityBase:

default T getCloneAndSaveFor(UUID uuid)
throws Exception {
    T entity = getByUuid(uuid);
    return getCloneAndSave(entity);
}

Другая возможность - написать отдельный метод в GenericEntityCloneService, который знает, что каждый EntityCloneService имеет определенный тип, дажеесли этот конкретный тип сам по себе не известен:

private <E extends AbstractNamedEntityBase> E getCloneAndSaveFor(
    UUID uuid,
    EntityCloneService<E> service)
throws Exception {

    E entity = service.getByUuid(uuid);
    return service.getCloneAndSave(entity);
}

Примечания:

  • throws Exception не является хорошей практикой.Не заставляйте абонентов перехватывать непроверенные исключения, которые предназначены для выявления ошибок программиста, таких как NullPointerException, IndexOutOfBoundsException, IllegalArgumentException и т. Д.
  • Вы можете сократить объявление Map до private static final Map<String,EntityCloneService<? extends AbstractNamedEntityBase>> registration = new HashMap<>();.
0 голосов
/ 04 декабря 2018

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

public <S> S getCloneAndSave(S existingEntity) throws Exception;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...