Проблема с типичным типом возврата в инъекции с укосом factory

До сих пор я успешно использовал google guice 2. Во время перехода на guice 3.0 у меня были проблемы с вспомогательными заводами для инъекций. Предположим, что следующий код

public interface Currency {}
public class SwissFrancs implements Currency {}

public interface Payment<T extends Currency> {}
public class RealPayment implements Payment<SwissFrancs> {
    @Inject
    RealPayment(@Assisted Date date) {}
}

public interface PaymentFactory {
    Payment<Currency> create(Date date);
}

public SwissFrancPaymentModule extends AbstractModule {
    protected void configure() {
        install(new FactoryModuleBuilder()
             .implement(Payment.class, RealPayment.class)
             .build(PaymentFactory.class));
    }
}

При создании инжектора я получаю следующее исключение:

com.google.inject.CreationException: Guice creation errors:

1) Payment<Currency> is an interface, not a concrete class.
   Unable to create AssistedInject factory. while locating Payment<Currency>
   at PaymentFactory.create(PaymentFactory.java:1)

С помощью помощника создателя инъекций из guice 2 моя конфигурация работает:

bind(PaymentFactory.class).toProvider(
FactoryProvider.newFactory(PaymentFactory.class, RealPayment.class));

Единственным обходным решением, которое я нашел до сих пор, является удаление общего параметра из возвращаемого типа метода factory:

public interface PaymentFactory {
    Payment create(Date date);
}

Знает ли кто-нибудь, почему в goice 3 не нравится общий параметр в методе factory или то, что я вообще неправильно понял о вспомогательных заводах для инъекций? Спасибо!

Ответ 1

В вашем коде есть две проблемы.

Во-первых, RealPayment реализует Payment<SwissFrancs>, но PaymentFactory.create возвращает Payment<Currency>. A Payment<SwissFrancs> не может быть возвращен из метода, который возвращает Payment<Currency>. Если вы измените тип возврата create на Payment<? extends Currency>, тогда RealPayment будет работать (потому что он Payment для чего-то, что расширяет Currency).

Во-вторых, вам нужно использовать версию implement, которая принимает в качестве первого аргумента TypeLiteral. Способ сделать это - использовать анонимный внутренний класс. Чтобы представить "Payment", вы можете использовать

new TypeLiteral<Payment<? extends Currency>>() {}

Для получения дополнительной информации см. Javadoc для этого конструктора TypeLiteral.