繁体   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