powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Java [игнор отключен] [закрыт для гостей] / Реализация Factory с Generic
15 сообщений из 15, страница 1 из 1
Реализация Factory с Generic
    #38477277
abc_da
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Добрый день,

У меня есть иерархия объектов и иерархия обработчиков. Я пытаюсь сделать параметризованный Factory-метод и столкнулся со сложностями. В аттаче находится проект, иллюстрирующий проблему.

Допустим, вот мои объекты:
Код: java
1.
2.
3.
4.
5.
public interface Fruit {
}

public class Apple implements Fruit {
}



А это мои обработчики:
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
public interface FruitHandler<T extends Fruit> {
    T handle(T fruit);
}

public class AppleHandler implements FruitHandler<Apple> {

    @Override
    public Apple handle(Apple fruit) {
        return null; // do something
    }
}



Я хочу, чтобы мой factory-метод выглядел примерно вот так:
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
public class HandlerFactory {
    
    public static FruitHandler<? extends Fruit> getHandler(Class<? extends Fruit> fruitClazz) {
        
        if(Apple.class.isAssignableFrom(fruitClazz)) {
            return new AppleHandler();
        }
        
        return null;
    }
}



И чтобы его можно было использовать примерно так:
Код: java
1.
2.
3.
4.
        FruitHandler<? extends Fruit> handler = HandlerFactory.getHandler(Apple.class);

        Apple apple = new Apple();
        Apple handledApple = handler.handle(apple);



Но последняя строка дает ошибку компиляции:
Код: java
1.
handle (capture<? extends Fruit>) in FruitHandler cannot be applied to (Apple)



Вопросы:
1. Почему возникает эта ошибка?
2. Возможно ли добиться желаемого мной дизайна без explicit cast'ов?
...
Рейтинг: 0 / 0
Реализация Factory с Generic
    #38477680
maxkar
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
abc_da,

Ошибка возникает потому, что "?" в левой и правой части вашего метода не связаны. По сигнатурам вам нужно что-то вроде:
Код: java
1.
2.
3.
 public static <T extends Fruit> FruitHandler<T> getHandler(Class<T> fruitClazz) {
  ...
}
...
Рейтинг: 0 / 0
Реализация Factory с Generic
    #38477720
ivanra
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
тут сразу 2 ошибки.
Код: java
1.
public static FruitHandler<? extends Fruit> getHandler(Class<? extends Fruit> fruitClazz)


1) генерик на входе
2) на выходе генерик с неизвестным типом (даже ничем не параметризованный)
Тема долгая, боюсь наделать ошибок, лучше обратиться к авторитетным книжкам

Я бы предложил подход, используемый в шаблоне "посетитель". Заодно можно избежать многоэтажных if-конструкций:
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
public class HandlerFactory {
  public static FruitHandler<Apple> getHandler(Apple fruit) {
    return new AppleHandler();
  }
  public static FruitHandler<Pineapple> getHandler(Pineapple fruit) {
    return new PineappleHandler();
  }
// и т.д, а в конце - метод, закрывающий все остальные неизвестные типы	
  public static FruitHandler<Fruit> getHandler(Fruit fruit) {
    return null;
  }
}


ну и работа с этим:
Код: java
1.
2.
3.
4.
        Apple apple = new Apple();
        FruitHandler<Apple> handler = HandlerFactory.getHandler(apple);

        Apple handledApple = handler.handle(apple);
...
Рейтинг: 0 / 0
Реализация Factory с Generic
    #38477874
abc_da
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Спасибо за ответы

maxkar,
так я тоже пробовал, но эффект такой же

ivanra,
спасибо, вы зрите в корень, мой следующий вопрос был бы "как избавиться от цепочки if-ов". К сожалению, предложенный вами вариант работает только если знать на момент компиляции, какого типа объект придет (как в моем упрощенном примере). Но в реальной жизни у меня нет такой информации, поэтому мне нужно разобраться с wildcard'ами, насколько я понимаю.
...
Рейтинг: 0 / 0
Реализация Factory с Generic
    #38477963
ivanra
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
abc_da,
так типизация как раз и предназначена для контроля типов на момент компиляции. Если у вас о конкретном типе становится известно только во время выполнения, то зачем все эти мучения? В этом случае интерфейс FruitHandler должен быть нетипизированным
Код: java
1.
2.
3.
interface FruitHandler {
    Fruit handle(Fruit fruit);
}


Если же имеется в виду работа с классами - наследниками, то тут как раз все работает. Например, анонимный класс-наследник:
Код: java
1.
2.
3.
        Apple apple = new Apple(){/* это анонимный класс - наследник */};
        FruitHandler<Apple> handler = HandlerFactory.getHandler(apple);
        Apple handledApple = handler.handle(apple);
...
Рейтинг: 0 / 0
Реализация Factory с Generic
    #38477974
javapecker
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
abc_da,
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
public class Apple implements Fruit {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class HandlerFactory {
    
    public static <T extends Fruit> FruitHandler getHandler(Class<T> fruitClazz) {
        
        if(Apple.class.isAssignableFrom(fruitClazz)) {
            return new AppleHandler();
        }
        
        return null;
    }
}

public class AppleHandler implements FruitHandler<Apple> {

    @Override
    public Apple handle(Apple fruit) {
        fruit.setName("Handled apple");
        return fruit;
    }
}

public class HandlerFactoryTest {
    
    @Test
    public void testFactory() {


        Apple apple = new Apple();
        Apple handledApple = handle(apple);
        System.out.println(apple.getName());
    }

    public <T extends Fruit> T  handle (T f){
        FruitHandler<T> handler = HandlerFactory.getHandler(f.getClass());
        return handler.handle(f);
    }
}
...
Рейтинг: 0 / 0
Реализация Factory с Generic
    #38479821
abc_da
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ivanra,
Не согласен, на мой взгляд, FruitHandler должен быть типизированным, т.к. когда дело доходит до него, тип уже известен (если не использовать фабрику). Вероятно, фабричный метод должен иметь сигнатуру с нетипизированным возвращаемым FruitHandler (как предложил javapecker). Но я в принципе не понимаю, почему не получается сделать его типизированным через <? extends Fruit>.

Почему new AppleHandler() приходится явно приводить к FruitHandler<T> ?
И почему <T extends Fruit> и <? extends Fruit> - не одно и то же?

Буду благодарен за ссылки на мат.часть.
...
Рейтинг: 0 / 0
Реализация Factory с Generic
    #38479829
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
Реализация Factory с Generic
    #38479844
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Почему бы так не сделать? Понятно, что связность немного больше. Но в данном примере я вообще не вижу чему бы это мешало.
Код: java
1.
2.
3.
4.
public abstract class Fruit {
    Handler getHandler();
    Class<? extends Handler> getHandlerClass(); //Если не хочется инстанциировать внутри Fruit 
}
...
Рейтинг: 0 / 0
Реализация Factory с Generic
    #38479856
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А ещё ошибка вот тут
Код: java
1.
public class AppleHandler implements FruitHandler<Apple>

Потому что по логике factory
нужно
Код: java
1.
public class AppleHandler implements FruitHandler<? extends Apple>


либо
Код: java
1.
public class AppleHandler<T> implements FruitHandler<T extends Apple>
...
Рейтинг: 0 / 0
Реализация Factory с Generic
    #38479868
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
javapecker
Код: java
1.
2.
        FruitHandler<T> handler = HandlerFactory.getHandler(f.getClass());
        return handler.handle(f);


Unchecked assignment
...
Рейтинг: 0 / 0
Реализация Factory с Generic
    #38479881
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Точнее так. Без этого AppleHandler теряет информацию о любом наследнике Apple
Код: java
1.
public class AppleHandler<T extends Apple> implements FruitHandler<T>
...
Рейтинг: 0 / 0
Реализация Factory с Generic
    #38479889
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А ещё при явном конструировании какого-то Handler-а в Factory, обязательно нужно кастить, так как фабрика расчитана на более широкий даиапазон типов, и Generics не могут уже дать гарантий. Гарантии программист пишет явно в виде isAssignableFrom. Компилятор о них догадаться не может.
...
Рейтинг: 0 / 0
Реализация Factory с Generic
    #38480053
ivanra
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
abc_da,
типизация интерфейса FruitHandler приводит к необходимости явно знать всю информацию о типах во время компиляции, поэтому хорошо подумайте, нужна ли она.
В яве наследование в генерике никак не учитывается, и тут ничего не поделаешь:
Код: java
1.
2.
3.
4.
        Collection<Number> n1;
        Collection<BigDecimal> d1;
        d1 = c1; // ошибка компиляции
        c1 = d1; // тоже ошибка


Ваше стремление типизировать всё подряд приводит к сильной связанности кода, если фабрика возвращает что-то большее, чем FruitHandler<Fruit>
Допустим, имеется:
- изменяемая часть программы - набор классов-наследников Fruit, набор их обработчиков, фабрика
- неизменяемая часть - некий бизнес код, использующий фабрику и то, что она производит
Так вот, бизнес код у вас сейчас должен изменяться с изменяемой частью. Добавление любого нового класса/обработчкика ведет к его добавлению в бизнес-код. Так или иначе, там должно появиться что-то типа
NewFruit, NewHandler, и даже Newhandler<NewFruit>

По-любому плохо. Остается FruitHandler<Fruit>, а поскольку это масло масленное, то достаточно возвращать просто FruitHandler - так мы и пришли к выводу о лишней типизации интрфейса. Вы можете объяснить для чего она понадобилась?
...
Рейтинг: 0 / 0
Реализация Factory с Generic
    #38480139
ivanra
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ну просто хочется понять - в чем смысл?
- используем генерики - хотим контролировать классы на этапе компиляции
- используем фабрику (обычно это делается для манипулирования объектами на абстрактном уровне), то есть не хотим контролировать классы на этапе компиляции
Единственный смысл, который я тут вижу - контроль котго, чтобы метод FruitHandler.handle возвращал объект того же типа, что и аргумент. так и следует типизировать сам метод
Код: java
1.
2.
3.
interface FruitHandler {
	<T extends Fruit> T handle(T fruit);
}
...
Рейтинг: 0 / 0
15 сообщений из 15, страница 1 из 1
Форумы / Java [игнор отключен] [закрыт для гостей] / Реализация Factory с Generic
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]