我的这个通用 Guice 绑定方法的 TypeLiteral 等价物有什么问题?

发布于 2024-12-23 16:02:41 字数 2015 浏览 1 评论 0 原文

以下通用 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 这样的通用参数,那么我假设我需要在 bindMultibinder superClass 参数中传递一个 TypeLiteral 。这是迄今为止我最好的尝试:

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

您有如何正确创建集合的想法吗?

The following generic Guice binding method behaves correctly:

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

And uses client code like this:

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

However, if I want to allow Option take a generic parameter like Option<Radio>, then I assume I need to pass a TypeLiteral in the bindMultibinder superClass parameter. This is my best attempt so far:

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

The binding code equivalent to the prior case looks like this:

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

I'm almost certain that the below binding is incorrect, because Types.setOf(superClass.getRawType()) returns a ParameterizedType

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

Any ideas how to create the set correctly?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

残龙傲雪 2024-12-30 16:02:41

ParameterizedType 是一个 java 类,用于表示在 java 源代码中需要用尖括号编写的类型:例如 FooSet<; 等类型。 Option>Set> 甚至 Set>。这就是你想要的返回值。

事实上,您所做的工作只需在倒数第二行中调用 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>() {});

虽然现在考虑一下,我想知道您是否真的想要一个采用 TypeLiteralbindMultibinder 重载>。您不想拥有一个需要 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,您可能会让此方法的调用者在尝试使用您的返回值时抑制非类型化警告。最好在这里执行此操作,您可以将警告抑制限制为一行,并且可以证明强制转换是安全的。

ParameterizedType is the java class that is used to represent types that in java source code you need to write with angle brackets: types like Foo<Bar> or Set<Option> or Set<Option<Radio>> or even Set<? extends Option<Radio>>. That is the return value you want.

What you've done will in fact work correctly with the very minor change that you want to call superClass.getType() in your next-to-last line instead of superClass.getRawType(). That being said, while I'm here I do have a few other suggestions.

First off, in your first method I'd change it to:

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

That will let you do calls like this:

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

Or, if you aren't using guava - though you should be - you can use Arrays.asList instead of ImmutableList.of. You get the same amount of type safety as before without needing all those angle bracket declarations in your binding code.

If you don't have many callers of bindMultibinder yet, I'd also swap the order of the arguments, but that might just be a personal style thing.

With these same changes, your second method becomes:

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

And you can use it similarly:

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

Though thinking about it now, I wonder if you really want an overload of bindMultibinder that takes a TypeLiteral. Wouldn't you rather have one that takes a Key instead?

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

After all, you can call this method in almost the same fashion:

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

Except that Key is easier to type than TypeLiteral, and if you need to put in something that's identified only by its annotation, that's trivial to do:

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

Now, is that @Suppress making you nervous? Good instincts.

Unfortunately, the sad fact is that when dealing with reflection around generified types - types with angle brackets in them - you're almost certainly going to have small unchecked bits. My suggestion is that you make the bit that needs to have untyped warnings suppressed as small as possible, and expose to the outside world as much type information as you can. If you return a Key<?> from here, you're likely going to make the caller of this method suppress untyped warnings when they try to use your return value. Better to do it here, where you can limit the warnings suppression to a single line, and one where you can prove that the cast is safe.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文