Java generics — использовать один и тот же подстановочный знак несколько раз

#java #generics #wildcard

#java #дженерики #подстановочный знак

Вопрос:

У меня есть объявление класса, в котором используются общие и ограниченные подстановочные знаки:

 class Factory<T extends Logic<? extends Entity>, 
              U extends DAO<? extends Entity>> 
{
}
  

По сути, это универсальная фабрика, которая принимает логический интерфейс (T) и возвращает настроенную реализацию. Для создания экземпляра логики я беру соответствующий класс DAO, реализующий интерфейс DAO (U).

Оба интерфейса для logic и DAO также являются общими и принимают тип объекта для работы в качестве параметра типа. Однако я хочу ограничить это еще больше, чтобы DAO и Logic не только имели параметр типа, который расширяет Entity, но и расширяли одну и ту же сущность. Результат может выглядеть примерно так:

 class <X extends Entity> Factory<T extends Logic<X>, 
              U extends DAO<X>> 
{
}
  

Могу ли я достичь этого с помощью Java generics?

Ответ №1:

Да, вы близки к цели. Сделайте это следующим образом:

 class Factory<X extends Entity,
              T extends Logic<X>, 
              U extends DAO<X>> 
{
}
  

Альтернатива

 class Factory<T extends Logic<?>, 
              U extends DAO<?>> 
{
    // Here, the generic method parameter only requires X
    // to be the same bound at method invocation. However,
    // you will "lose" that information again when the 
    // Factory is returned.
    public static <X extends Entity,
                   T extends Logic<X>, 
                   U extends DAO<X>> Factory<T, U> createFactory(T logic, U dao)
    {
        return new Factory<T, U>(logic, dao);
    }
}
  

Комментарии:

1. Вы также должны сделать это, если у вас этого еще нет: interface Logic<T extends Entity> .

2. Я думаю, вы пропустили там запятую после объекта. Ваше решение будет работать, но нет ли решения, в котором мне не нужно вводить параметр третьего типа? Ваша версия Factory будет создана следующим образом: Factory<MyEntity, MyLogic, MyDAO> = new Factory<MyEntity, MyLogic, MyDAO>(); — верно? @ Thomas: Параметр типа Logic / DAOs уже ограничен, вот почему я чувствую, что дополнительный параметр типа в Factory для отношения излишен.

3. Это зависит. Смотрите мой вариант. Там вы можете потребовать, чтобы X была одинаковая привязка для обоих Logic и DAO . Но как только вы сделаете ссылку Factory , вы, конечно, снова потеряете эту информацию, если она не будет помещена в сам класс Factory

4. @atamanroman Проблема в том, что универсальные типы не могут ссылаться на универсальные параметры друг друга, но вы все равно захотите передать один и тот же класс сущностей, а не что-то вроде Factory<Logic<FooEntity>, DAO<BarEntity>> if FooEntity и BarEntity не связаны.

Ответ №2:

Другим подходом могло бы быть предоставление оболочки (хотя это не совсем элегантно ;)):

 class Entity{}

interface Logic<T extends Entity> {}

interface DAO<T extends Entity> {}

interface DaoLogic<X extends Entity> {
  DAO<X> getDAO();
  Logic<X> getLogic();
}

class Factory<T extends DaoLogic<? extends Entity>> {}
  

Ответ №3:

Сработает ли следующее. X был бы «общим» типом, где Logic и DAO оба использовали бы этот тип.

 public class Factory<X extends Entity, T extends Logic<X>, U extends DAO<X>>
{
}