Использование неизменяемой коллекции гуавы в качестве параметра метода и / или типа возвращаемого значения - PullRequest
15 голосов
/ 01 марта 2012

Я пытаюсь определить, какие лучшие практики будут для ImmutableList. Ниже приведен упрощенный пример, который поможет ответить на мои вопросы:

Ex:

public ImmutableCollection<Foo> getFooOne(ImmutableList<Foo> fooInput){ 
   //.. do some work
   ImmutableList<Foo> fooOther = // something generated during the code
   return fooOther;
}

public Collection<Foo> getFooTwo(List<Foo> fooInput){
   //.. do some work
   List<Foo> fooOther = // something generated during the code
   return ImmutableList.copyOf(fooOther);
}

public void doSomethingOne(){
  ImmutableCollection<Foo> myFoo = getFooOne(myList);
  ...
  someOtherMethod(myFoo);
}

public void doSomethingTwo(){
  Collection<Foo> myFoo = getFooOne(myList);
  ...
  someOtherMethod(myFoo);
}

Мои вопросы:

  1. Что имеет смысл использовать в приложении? [doSomethingOne и getFooOne] или [doSomethingTwo and fooTwo]? Другими словами, если вы знаете, что используете ImmutableCollections, имеет ли смысл продолжать приводить туда-сюда и выполнять copyOf () или просто использовать Immutable везде?

  2. Эти примеры являются публичными методами, которые могут подразумевать, что их используют другие люди. Изменится ли какой-либо из этих ответов, если методы будут частными и будут использоваться внутри?

  3. Если пользователь попытается добавить что-либо в неизменяемый список, будет сгенерировано исключение. Поскольку они могут не знать об этом, не имеет ли больше смысла явно возвращать ImmutableCollection вместо Collection?

Ответы [ 5 ]

18 голосов
/ 01 марта 2012

В общем случае, не стоит фиксировать конкретную реализацию в объявленном вами типе возвращаемого значения, но мы думаем о неизменяемых типах как об исключении. Есть несколько причин для объявления типа возврата Immutable*:

  • Они документируют, что вы возвращаете снимок, а не просмотр в реальном времени.
  • Они подтверждают, что звонящий не может изменить результат.
  • Они документируют, что порядок вставки сохраняется (что может или не может быть значительным в вашем случае использования).
  • Они документируют, что коллекция не будет содержать null.
  • Кто-то может захотеть метод asList() или reverse().
  • Вы можете сохранить кому-либо вызов copyOf(), если он хочет назначить поле Immutable*. (Но учтите, что, если он включает copyOf(), он закоротит большинство неизменных входов, даже если вы не объявляете тип возвращаемого значения.)

По сути, я просто набираю с https://github.com/google/guava/wiki/TenThingsAboutImmutableCollections,, который вы можете проверить полностью.

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

Если я понял ваши намерения, то правильный способ создания getFooXxx для создания неизменяемой копии возможно-изменяемого списка выглядит примерно так:

/**
 * Returns an <b>immutable copy</b> of input list.
 */
public ImmutableList<Foo> immutableCopyOfFoo(List<Foo> input){
    return ImmutableList.copyOf(input);
}

Почему?

  • ImmutableList.copyOf() делает магию , когда данный список неизменен,
  • подпись метода явно говорит о том, что он делает,
  • метод возвращает ImmutableList, который является , на самом деле ImmutableCollection, но почему вы хотите скрыть информацию о ImmutableList от пользователя? Если он хочет, он вместо этого напишет Iterable foo = immutableCopyOfFoo(mutableFoo);, но на 99% он будет использовать ImmtableList,
  • возвращая ImmutableList дает обещание - «Я неизменен, и я все взорву, если вы попытаетесь изменить меня!»
  • и последнее, но не менее важное - предлагаемый метод не нужен для внутреннего использования; просто используйте

    someOtherMethod(ImmutableList.copyOf(foo));
    

    прямо в вашем коде ...


Вы должны проверить @ ответ ChrisPovirk (и ссылку на вики в этом ответе), чтобы знать, что, например, когда List<Foo> input содержит null s, вы получите неприятный NPE во время выполнения, если попытаетесь неизменная копия ...


РЕДАКТИРОВАТЬ ответ на комментарий № 1:

Collection контракт менее строг, чем List контракт; т.е. коллекция не гарантирует какой-либо порядок элементов («некоторые упорядочены, а другие неупорядочены»), в то время как List это делает («упорядоченная коллекция (также известная как последовательность)»).

Если вход представляет собой Список , это говорит о том, что порядок важен, и поэтому выходные данные должны гарантировать то же самое. Представьте себе, что:

public ImmutableCollection<Foo> immutableCopyOfFoo(List<Foo> input){
    return ImmutableSortedSet.copyOf(input, someFancyComparator);
}

Это не пахнет правильно. Если вас не интересует порядок, возможно, подпись метода должна быть immutableCopyOfFoo(Collection<Foo> input)? Но это зависит от конкретного варианта использования.

1 голос
/ 01 апреля 2014

Мой обычный подход:

  • принять список параметров (чтобы интерфейс был проще для клиентов)

  • если важны производительность / использование памяти / безопасность потоков, скопируйте содержимое предоставленного списка в структуру данных, оптимизированную для использования вашим классом

  • при возврате ImmutableList, тип возвращаемого значения должен быть ImmutableList (поскольку он дает вызывающей стороне больше информации о том, как он может использовать возвращаемое значение)

  • при возврате изменяемой реализации List, List должен быть типом возврата, если только не важно что-то еще о типе возврата (безопасность потока, как плохой * пример)

* Это плохой пример, потому что если ваши возвращаемые значения должны быть потокобезопасными, это, вероятно, означает, что с вашим кодом что-то не так.

Замените List / ImmutableList любым типом неизменяемой коллекции.

1 голос
/ 01 марта 2012
public ImmutableCollection<Foo> getFooOne(ImmutableList<Foo> fooInput){
   ImmutableList<Foo> fooOther= fooInput;
   return ImmutableList.copyOf(fooOther);
}

Это не имеет никакого смысла вообще.Зачем вам копировать неизменную коллекцию?Весь смысл неизменности заключается в том, что его нельзя изменить, поэтому вы можете использовать его снова.

public Collection<Foo> getFooTwo(List<Foo> fooInput){
   ImmutableList<Foo> fooOther= ImmutableList.copyOf(fooInput);
   return ImmutableList.copyOf(fooOther);
}

???Зачем это делать дважды ???Это нормально:

public Collection<Foo> getFooTwo(List<Foo> fooInput){
   return ImmutableList.copyOf(fooInput);
}

ImmutableList.copyOf(Collection) достаточно умен, чтобы вернуть ImmutableList без изменений и создать новый ImmutableList для всего остального.

0 голосов
/ 01 марта 2012

Вы всегда должны использовать стандартные классы JRE на общедоступных интерфейсах.В классах Guava Immutable... нет никаких дополнительных методов, поэтому вы не получаете никакой безопасности во время компиляции: любые попытки внести изменения в эти объекты потерпят неудачу только во время выполнения (но см. Комментарий Барта).Вы должны документировать в методах, которые возвращают коллекции, что они неизменны.

Вы должны делать защитные копии списков, предоставляемых в открытых методах, если вы беспокоитесь о одновременном изменении, но можно указать ImmutableCollection для частногоаргументы метода.

...