Что не так с моим эквивалентом TypeLiteral этого универсального метода привязки Guice? - PullRequest
2 голосов
/ 31 декабря 2011

Следующий универсальный метод привязки 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 в параметре bindMultibinder superClass.Пока это моя лучшая попытка:

<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 Ответ

2 голосов
/ 13 марта 2012

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

Или, если вы не используете гуава - хотя вы должны это сделать - вы можете использовать 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>() {});

За исключением того, что Key легче набрать, чем TypeLiteral, и если вам нужно вставить что-то, что идентифицируется только его аннотацией, это тривиально:

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<?> отсюда, вы, вероятно, заставите вызывающего этого метода подавлять нетипизированные предупреждения, когда они пытаются использовать ваше возвращаемое значение. Лучше сделать это здесь, где вы можете ограничить подавление предупреждений одной строкой, а другой - доказать, что приведение является безопасным.

...