【问题标题】:How do I bind the Interface with Implementation for Generic Classes?如何将接口与泛型类的实现绑定?
【发布时间】:2015-08-05 17:29:36
【问题描述】:

[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 会有这种奇怪的行为?为什么无论工厂如何,我都无法将接口与实现类相关联?

【问题讨论】:

    标签: java generics inheritance dependency-injection guice


    【解决方案1】:

    我无法确切地弄清楚您要做什么,但在我看来,您实际上并没有尝试创建 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
    

    如果您正在尝试使用 AssistedInject 构建 Class1 对象的工厂,那么由于您指定基因类型参数的方式,这要困难得多。不幸的是,你不能inject a generic factory using AssistedInject in the normal way,这会加倍困难,因为你的Class1 实现需要T1 extends Number,但你的接口可以是任何你想要的T1。这意味着您的工厂必须将其接收的输入参数的类型限制为Number 的子类;换句话说,你的工厂不能创建它想要的任何InterfClass1,它只能创建具有正确输入参数的实例。

    希望答案的前半部分是您想要做的。如果不是,我们需要有关您的用例的更多信息才能回答这个问题。

    【讨论】:

    • 感谢您的精彩回复!但是,我不明白为什么我不能对使用工厂做同样的事情,特别是考虑到如果我删除接口和类之间的绑定并将这个绑定添加到工厂的“配置”中(.implement(...)),它有效。请查看我以“编辑后”开头的更新文本。
    猜你喜欢
    • 2020-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-12
    • 1970-01-01
    • 2015-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多