关于 java:如何让 Guice 正确绑定这个泛型?

How do I make Guice bind this generic correctly?

我正在开发一个处理消息的平台,但我遇到了 Guice 绑定的问题。我编写了一个示例项目来显示我遇到的问题,这是一个如下所示的异常:

1) com.icacarealign.worker_example.PreprocessorMiddleware<MessageType> cannot be used as a key; It is not fully specified.

代码来了。

接口

1
2
3
4
5
6
7
8
9
10
11
public interface Middleware {
  void configure();
}

public interface PreprocessorMiddleware<MessageType> extends Middleware {
  void prework(Context<MessageType> contextObj);
}

public interface PostprocessorMiddleware<MessageType> extends Middleware {
  void postwork(Context<MessageType> contextObj);
}

古斯绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class MiddlewareModule<MessageType> extends AbstractModule {
  private final List<Class<? extends PostprocessorMiddleware<MessageType>>> _postprocessors;
  private final List<Class<? extends PreprocessorMiddleware<MessageType>>> _preprocessors;

  public MiddlewareModule(
    List<Class<? extends PreprocessorMiddleware<MessageType>>> preprocessors,
    List<Class<? extends PostprocessorMiddleware<MessageType>>> postprocessors
  ) {
    _preprocessors = preprocessors;
    _postprocessors = postprocessors;
  }

  @Override
  protected void configure() {
    bindAllMiddleware(_preprocessors, new TypeLiteral<PreprocessorMiddleware<MessageType>>() {});
    bindAllMiddleware(_postprocessors, new TypeLiteral<PostprocessorMiddleware<MessageType>>() {});
  }

  private <T extends Middleware> void bindAllMiddleware(List<Class<? extends T>> middleware, TypeLiteral< T > type) {
    Multibinder< T > multibinder = Multibinder.newSetBinder(binder(), type);

    middleware.forEach(middlewareType -> bindMiddleware(multibinder, middlewareType));
  }

  private <T extends Middleware> void bindMiddleware(Multibinder< T > binder, Class<? extends T> type) {
    binder().bind(type).in(Singleton.class);
    binder.addBinding().to(type);
  }
}

主要方法

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {
  public static void main(String[] args) {
    List<Class<? extends PreprocessorMiddleware<Message>>> preprocessorMiddlewares = new ArrayList<>();
    List<Class<? extends PostprocessorMiddleware<Message>>> postprocessorMiddlewares = new ArrayList<>();

    preprocessorMiddlewares.add(ArbitraryPrepreprocessorMiddleware.class);
    postprocessorMiddlewares.add(ArbitraryPostprocessorMiddleware.class);

    MiddlewareModule<Message> module = new MiddlewareModule<>(preprocessorMiddlewares, postprocessorMiddlewares);

    Injector injector = Guice.createInjector(module);
  }
}

我做错了什么?


正如 jacobm 所描述的,您需要解决泛型问题,并且可能需要传入您要绑定的类型。幸运的是,您可以使用 Guice 的 Types.newParameterizedType 来创建完全指定的 TypeLiteral。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class MiddlewareModule<MessageType> extends AbstractModule {
  private final Class<MessageType> _clazz;
  private final List<Class<? extends PostprocessorMiddleware<MessageType>>> _postprocessors;
  private final List<Class<? extends PreprocessorMiddleware<MessageType>>> _preprocessors;

  public MiddlewareModule(
    // Accept the message type in a way that survives erasure.
    Class<MessageType> clazz,
    List<Class<? extends PreprocessorMiddleware<MessageType>>> preprocessors,
    List<Class<? extends PostprocessorMiddleware<MessageType>>> postprocessors
  ) {
    _clazz = clazz;
    _preprocessors = preprocessors;
    _postprocessors = postprocessors;
  }

  @Override
  protected void configure() {
    // Use the Class to create your fully-specified TypeLiteral.
    bindAllMiddleware(_preprocessors,
        Types.newParameterizedType(PreprocessorMiddleware.class, _clazz));
    bindAllMiddleware(_postprocessors,
        Types.newParameterizedType(PostprocessorMiddleware.class, _clazz));
  }

  private <T extends Middleware> void bindAllMiddleware(List<Class<? extends T>> middleware, TypeLiteral< T > type) {
    Multibinder< T > multibinder = Multibinder.newSetBinder(binder(), type);

    middleware.forEach(middlewareType -> bindMiddleware(multibinder, middlewareType));
  }

  private <T extends Middleware> void bindMiddleware(Multibinder< T > binder, Class<? extends T> type) {
    bind(type).in(Singleton.class);  // Don't call binder() explicitly.
    binder.addBinding().to(type);
  }
}

请注意,这是未经测试的代码,因为我没有完整的 SSCCE;您可能需要调整类型参数以使 Guice 相信您在泛型中使用通配符是安全的。


您不能在 TypeLiteral 中使用类型变量(在本例中为 MessageType)用于 Guice 绑定。相反,您需要重构以传递一个在不使用任何类型变量的情况下构造的 TypeToken(可能这意味着从调用站点传递它)。

这是一个类似情况的更复杂的答案。结果是,如果你真的需要避免在每个调用点都使用类型文字,你可以再抽象一点,但这会带来相当高的复杂性成本。