我的TypeLiteral等效于这种通用Guice绑定方法有什么问题?

时间:2011-12-30 20:16:31

标签: java generics types guice

以下通用Guice绑定方法行为正确:

<T> Key<?> bindMultibinder(
    ArrayList<Class<? extends T>> contents, Class<T> superClass) {
   Named annotation = randomAnnotation();
   Multibinder<T> options = 
    Multibinder.newSetBinder(binder(), superClass, annotation);
   for (Class<? extends T> t : contents) {
      options.addBinding().to(t);
   }
   final Key<?> multibinderKey = Key.get(Types.setOf( superClass ), annotation);
   return multibinderKey;
}

使用这样的客户端代码:

ArrayList<Class<? extends Option>> options = 
 new ArrayList<Class<? extends Option>>();
options.add(CruiseControl.class);
bindMultibinder(options, Option.class);

但是,如果我想允许Option采用像Option<Radio>这样的通用参数,那么我假设我需要在TypeLiteral superClass参数中传递bindMultibinder。到目前为止,这是我最好的尝试:

<T> Key<?> bindMultibinder(
 ArrayList<TypeLiteral<? extends T>> contents, TypeLiteral<T> superClass) {
   Named annotation = randomAnnotation();
   Multibinder<T> options = 
    Multibinder.newSetBinder(binder(), superClass, annotation);
   for (TypeLiteral<? extends T> t : contents) {
      options.addBinding().to(t);
   }
   final Key<?> multibinderKey = Key.get(Types.setOf(superClass.getRawType()), annotation);
   return multibinderKey;
}

与先前案例等效的绑定代码如下所示:

ArrayList<TypeLiteral<? extends Option>> options = 
 new ArrayList<TypeLiteral<? extends Option>>();
options.add(new TypeLiteral<CruiseControl>(){});
bindMultibinder(options, new TypeLiteral<Option>(){});

我几乎可以肯定以下绑定不正确,因为Types.setOf(superClass.getRawType())会返回ParameterizedType

final Key<?> multibinderKey = 
 Key.get(Types.setOf(superClass.getRawType()), annotation);

有关如何正确创建设置的任何想法吗?

1 个答案:

答案 0 :(得分:2)

ParameterizedType是java类,用于表示需要使用尖括号编写的java源代码中的类型:Foo<Bar>Set<Option>Set<Option<Radio>>等类型甚至Set<? extends Option<Radio>>。这是你想要的返回值。

您所做的事实上可以正常使用您想要在倒数第二行而不是superClass.getType()中调用superClass.getRawType()的微小更改。话虽如此,当我在这里时,我还有其他一些建议。

首先,在您的第一种方法中,我将其更改为:

<T> Key<Set<T>> bindMultibinder(
    Iterable<? extends Class<? extends T>> contents, Class<T> superClass) {
   Named annotation = randomAnnotation();
   Multibinder<T> options = 
     Multibinder.newSetBinder(binder(), superClass, annotation);
   for (Class<? extends T> t : contents) {
      options.addBinding().to(t);
   }
   @SuppressWarnings("unchecked")
   final Key<Set<T>> multibinderKey = (Key<Set<T>>) Key.get(Types.setOf( superClass ), annotation);
   return multibinderKey;
}

这会让你做这样的电话:

bindMultibinder(ImmutableList.of(CruiseControlSubOptOne.class,
                                 CruiseControlSubOptTwo.class),
                Option.class);

或者,如果您没有使用guava - 虽然您应该使用 - 但您可以使用Arrays.asList代替ImmutableList.of。您可以获得与以前相同的类型安全性,而无需在绑定代码中使用所有尖括号声明。

如果你还没有很多bindMultibinder的来电者,我也会交换参数的顺序,但这可能只是个人风格的事情。

通过这些相同的更改,您的第二种方法变为:

<T> Key<Set<T>> bindMultibinder(
 Iterable<? extends TypeLiteral<? extends T>> contents, TypeLiteral<T> superClass) {
   Named annotation = randomAnnotation();
   Multibinder<T> options = 
    Multibinder.newSetBinder(binder(), superClass, annotation);
   for (TypeLiteral<? extends T> t : contents) {
      options.addBinding().to(t);
   }
   @SuppressWarnings("unchecked")
   final Key<Set<T>> multibinderKey = (Key<Set<T>>) Key.get(Types.setOf(superClass.getType()), annotation);
   return multibinderKey;
}

您可以类似地使用它:

bindMultibinder(ImmutableList.of(
                    new TypeLiteral<CruiseControlSubOptOne>() {},
                    new TypeLiteral<CruiseControlSubOptTwo>() {}),
                new TypeLiteral<Option>() {});

虽然现在正在思考,但我想知道你是否真的想要bindMultibinder的重载需要TypeLiteral。你宁愿选择一个需要Key吗?

<T> Key<Set<T>> bindMultibinder(Iterable<? extends Key<? extends T>> contents, Key<T> superClass) {
  Named annotation = randomAnnotation();
  Multibinder<T> options =
      Multibinder.newSetBinder(binder(), superClass.getTypeLiteral(), annotation);
  for (Key<? extends T> t : contents) {
    options.addBinding().to(t);
  }
  @SuppressWarnings("unchecked")
  final Key<Set<T>> multibinderKey =
      (Key<Set<T>>) Key.get(Types.setOf(superClass.getTypeLiteral().getType()), annotation);
  return multibinderKey;
}

毕竟,你可以用几乎相同的方式调用这个方法:

bindMultibinder(ImmutableList.of(
                    new Key<CruiseControlSubOptOne>() {},
                    new Key<CruiseControlSubOptTwo>() {}),
                new Key<Option>() {});

除了KeyTypeLiteral更容易输入,并且如果您需要输入仅由其注释标识的内容,那么这很简单:

bindMultibinder(ImmutableList.of(
                    new Key<CruiseControlSubOptOne>() {},
                    new Key<CruiseControlSubOptTwo>() {},
                    Key.get(CruiseControl.class, Names.named("ThirdOpt")),
                    Key.get(CruiseControl.class, Names.named("FourthOpt"))),
                new Key<Option>() {});

现在,@Suppress让你紧张吗?良好的直觉。

不幸的是,令人遗憾的事实是,当处理泛化类型的反射 - 其中带有尖括号的类型 - 你几乎肯定会有小的未经检查的位。我的建议是,你需要尽可能小地抑制需要禁用无类别警告的位,并尽可能多地向外界公开类型信息。如果您从此处返回Key<?>,那么当您尝试使用返回值时,您可能会使此方法的调用者禁止无类型警告。最好在这里做,你可以将警告抑制限制在一行,并且你可以证明演员是安全的。