简体   繁体   English

如何将接口与通用类的实现绑定?

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

[Guice 4.0] [Guice 4.0]

I would like to have an interface for the generic class and utilize it in the dependency injection with the use of Guice. 我想为通用类提供一个接口,并在使用Guice进行依赖项注入中使用它。 For the code listed below I get the following error: 对于下面列出的代码,我得到以下错误:

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());
    }
}

What causes this error? 是什么导致此错误?

After editing 编辑后

Let's add the factory and modify the module (remove the InterfClass1 interface from the 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);
    }
}

It works just fine! 它很好用!

Let's now incorporate the interface: 现在让我们合并接口:

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);
    }
}

And we get: 我们得到:

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)

Ok, let's, regardless of the error, also change the factory: 好吧,让我们不管错误如何,都更改工厂:

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

And we get: 我们得到:

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)

However, if I remove the relation between the interface and class in the module and add this relation into the specification of the factory, I get no error: 但是,如果删除模块中接口和类之间的关系,并将此关系添加到工厂的规范中,则不会出错:

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);
    }
}

It works fine! 工作正常!

Why Guice has this strange behaviour? 为什么Guice有这种奇怪的行为? Why I cannot relate the interface with the implementation class regardless of the factory? 为什么无论工厂如何,我都无法将接口与实现类相关联?

I can't figure out exactly what you're trying to do, but it looks to me like you aren't actually trying to create an AssistedInject factory, because you haven't created the interface for the factory. 我无法弄清楚您要做什么,但是在我看来,您实际上并不是在尝试创建AssistedInject工厂,因为您尚未创建工厂接口。 In this case, you don't need to use the @Assisted parameters at all. 在这种情况下,您根本不需要使用@Assisted参数。 Your constructor should look like this (with no other changes to the class): 您的构造函数应如下所示(该类没有其他更改):

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

Then, specify the bindings in your module. 然后,在模块中指定绑定。 You have to explicitly tell Guice what to inject for the arguments, and the annotation you've specified : 您必须明确地告诉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);
}

Then, we can run it like this: 然后,我们可以这样运行它:

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());
}

As you can see when running it, it will inject a Class implementation, with the two arguments as specified: 如您所见,在运行它时,它将注入Class实现,并带有指定的两个参数:

class guice.Class1
T1: 5 T2: 15

If you are trying to build a factory of Class1 objects, using AssistedInject, this is significantly more difficult because of the way you've specified the generit type parameters. 如果你正在试图建立一个工厂的Class1对象,使用AssistedInject,这是因为你所指定的generit类型参数的方式显著更加困难。 Unfortunately, you cannot inject a generic factory using AssistedInject in the normal way , and this is made doubly more difficult because your Class1 implementation requires T1 extends Number , but your interface can be any T1 you want. 不幸的是,您不能以常规方式使用AssistedInject注入泛型工厂 ,并且这变得更加困难,因为您的Class1实现需要T1 extends Number ,但是您的接口可以是您想要的任何T1 This means that your factory must restrict the type of the input arguments it receives to be subclasses of Number ; 这意味着您的工厂必须将接收到的输入参数的类型限制为Number子类; in other words, your factory can't create just any InterfClass1 it wants, it can only create instances with the proper input arguments. 换句话说,您的工厂不能只创建它想要的任何InterfClass1 ,它只能创建具有适当输入参数的实例。

Hopefully the first half of the answer is what you're trying to do. 希望答案的前半部分是您要执行的操作。 If it's not, we need more information about your use case to be able to answer this question. 如果不是,我们需要有关您的用例的更多信息,以便能够回答此问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM