簡體   English   中英

如何將接口與通用類的實現綁定?

[英]How do I bind the Interface with Implementation for Generic Classes?

[Guice 4.0]

我想為通用類提供一個接口,並在使用Guice進行依賴項注入中使用它。 對於下面列出的代碼,我得到以下錯誤:

Exception in thread "main" com.google.inject.CreationException: Unable to create injector, see the following errors:

1) Could not find a suitable constructor in com.ulmon.fsqtransit.guicetest.Class1. Classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private.
  at com.ulmon.fsqtransit.guicetest.Class1.class(Class1.java:14)
  at com.ulmon.fsqtransit.guicetest.Module.configure(Module.java:14)

-

public class Class1<T1 extends Number, T2 extends Number>
    implements InterfClass1<T1, T2> {
    public static final String ANNOT1 = "ANNOT1";
    public static final String ANNOT2 = "ANNOT2";
    private T1 t1;
    private T2 t2;
    // for the factory
    @AssistedInject
    public Class1(
            @Assisted(Class1.ANNOT1) T1 t1,
            @Assisted(Class1.ANNOT2) T2 t2
            ) {
        this.t1 = t1;
        this.t2 = t2;
    }
    public T1 getT1() {
        return t1;
    }
    public T2 getT2() {
        return t2;
    }
}


public class Module extends AbstractModule {
    @Override
    protected void configure() {

        bind(new TypeLiteral<InterfClass1<Integer, Integer>>(){})
            .to(new TypeLiteral<Class1<Integer, Integer>>(){});
    }

    public static void main(String[] args) {
        Injector inj = Guice.createInjector(new Module());
    }
}

是什么導致此錯誤?

編輯后

讓我們添加工廠並修改模塊(從模塊中刪除InterfClass1接口):

public interface Class1Factory<T1 extends Number, T2 extends Number> {
    public Class1<T1, T2> createClass1(
            @Assisted(Class1.ANNOT1) T1 t1,
            @Assisted(Class1.ANNOT2) T2 t2
            );
}

public class Module extends AbstractModule {
    @Override
    protected void configure() {
        install(new FactoryModuleBuilder()
            .build(new TypeLiteral<Class1Factory<Integer, Double>>(){}));
    }
    public static void main(String[] args) {
        Injector inj = Guice.createInjector(new Module());
        Class1Factory f = inj.getInstance(Key.get(new TypeLiteral<Class1Factory<Integer, Double>>(){}));
        f.createClass1(10, 11.0);
    }
}

它很好用!

現在讓我們合並接口:

public class Module extends AbstractModule {
    @Override
    protected void configure() {
        bind(new TypeLiteral<InterfClass1<Integer, Double>>(){})
            .to(new TypeLiteral<Class1<Integer, Double>>(){});
        install(new FactoryModuleBuilder()
            .build(new TypeLiteral<Class1Factory<Integer, Double>>(){}));
    }
    public static void main(String[] args) {
        Injector inj = Guice.createInjector(new Module());
        Class1Factory f = inj.getInstance(Key.get(new TypeLiteral<Class1Factory<Integer, Double>>(){}));
        f.createClass1(10, 11.0);
    }
}

我們得到:

1) Could not find a suitable constructor in com.ulmon.fsqtransit.guicetest.Class1. Classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private.
  at com.ulmon.fsqtransit.guicetest.Class1.class(Class1.java:15)
  at com.ulmon.fsqtransit.guicetest.Module.configure(Module.java:14)

好吧,讓我們不管錯誤如何,都更改工廠:

public interface Class1Factory<T1 extends Number, T2 extends Number> {
    public InterfClass1<T1, T2> createClass1(
            @Assisted(Class1.ANNOT1) T1 t1,
            @Assisted(Class1.ANNOT2) T2 t2
            );
}

我們得到:

1) Could not find a suitable constructor in com.ulmon.fsqtransit.guicetest.Class1. Classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private.
  at com.ulmon.fsqtransit.guicetest.Class1.class(Class1.java:15)
  at com.ulmon.fsqtransit.guicetest.Module.configure(Module.java:14)

2) com.ulmon.fsqtransit.guicetest.InterfClass1<java.lang.Integer, java.lang.Double> is an interface, not a concrete class.  Unable to create AssistedInject factory.
  while locating com.ulmon.fsqtransit.guicetest.InterfClass1<java.lang.Integer, java.lang.Double>
  at com.ulmon.fsqtransit.guicetest.Class1Factory.createClass1(Class1Factory.java:1)

但是,如果刪除模塊中接口和類之間的關系,並將此關系添加到工廠的規范中,則不會出錯:

public class Module extends AbstractModule {
    @Override
    protected void configure() {
        install(new FactoryModuleBuilder()
            .implement(new TypeLiteral<InterfClass1<Integer, Double>>(){}
                , new TypeLiteral<Class1<Integer, Double>>(){})
            .build(new TypeLiteral<Class1Factory<Integer, Double>>(){}));
    }
    public static void main(String[] args) {
        Injector inj = Guice.createInjector(new Module());
        Class1Factory f = inj.getInstance(Key.get(new TypeLiteral<Class1Factory<Integer, Double>>(){}));
        f.createClass1(10, 11.0);
    }
}

工作正常!

為什么Guice有這種奇怪的行為? 為什么無論工廠如何,我都無法將接口與實現類相關聯?

我無法弄清楚您要做什么,但是在我看來,您實際上並不是在嘗試創建AssistedInject工廠,因為您尚未創建工廠接口。 在這種情況下,您根本不需要使用@Assisted參數。 您的構造函數應如下所示(該類沒有其他更改):

@Inject
public Class1(
        @Named(Class1.ANNOT1) T1 t1,
        @Named(Class1.ANNOT2) T2 t2
        ) {

然后,在模塊中指定綁定。 您必須明確地告訴Guice要為參數以及指定的注釋注入什么:

@Override
protected void configure() {
    bind(new TypeLiteral<InterfClass1<Integer, Integer>>(){})
        .to(new TypeLiteral<Class1<Integer, Integer>>(){});
    bind(Integer.class)
        .annotatedWith(Names.named(Class1.ANNOT1))
        .toInstance(5);
    bind(Integer.class)
        .annotatedWith(Names.named(Class1.ANNOT2))
        .toInstance(15);
}

然后,我們可以這樣運行它:

public static void main(String[] args) {
    Injector inj = Guice.createInjector(new Module());
    InterfClass1<Integer, Integer> interf =
        inj.getInstance(Key.get(new TypeLiteral<InterfClass1<Integer, Integer>>(){}));
    System.out.println(interf.getClass());
    System.out.println("T1: " + interf.getT1() + " T2: " + interf.getT2());
}

如您所見,在運行它時,它將注入Class實現,並帶有指定的兩個參數:

class guice.Class1
T1: 5 T2: 15

如果你正在試圖建立一個工廠的Class1對象,使用AssistedInject,這是因為你所指定的generit類型參數的方式顯著更加困難。 不幸的是,您不能以常規方式使用AssistedInject注入泛型工廠 ,並且這變得更加困難,因為您的Class1實現需要T1 extends Number ,但是您的接口可以是您想要的任何T1 這意味着您的工廠必須將接收到的輸入參數的類型限制為Number子類; 換句話說,您的工廠不能只創建它想要的任何InterfClass1 ,它只能創建具有適當輸入參數的實例。

希望答案的前半部分是您要執行的操作。 如果不是,我們需要有關您的用例的更多信息,以便能夠回答此問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM