сохранение аргумента без изменений в вызове Java-метода - PullRequest
0 голосов
/ 21 февраля 2011

если я хочу вызвать метод, подобный следующему:

  List f(List l){
      l.add(new Object());
      return l;
  }

Все в порядке, за исключением того, что если я вызываю метод, он фактически изменяет свой аргумент, в любом случае?

есть ли способ объявить f таким образом, чтобы сохранить l неизменным в java?

Я знаю, что могу выполнить глубокий клон на l и передать его, но в случаях, когда l действительно большой, этооперация стоит дорого.

Ответы [ 5 ]

6 голосов
/ 21 февраля 2011

Ну, не вызывайте метод, который изменил бы его.Что бы вы ожидали от такого метода без копирования?Он должен либо вести себя по-другому (например, ничего не делать, когда вызывается add), либо выдавать исключение.Вы можете заставить его генерировать исключение, поместив его в неизменяемый список ... но если целью метода является изменение коллекции, вы, вероятно, не хотите, чтобы было сгенерировано исключение ...

Я знаю, что это звучит как банальный ответ, но я надеюсь, что он действительно доходит до сути того, о чем вы должны подумать: если у вас есть коллекция, которую не следует изменять, и вы хотитеЧтобы вызвать метод, который пытается изменить коллекцию, вы должны подумать, почему вы хотите вызвать этот метод в первую очередь.

Я понимаю, что сложная часть - это знать, какие методы будут измените коллекцию - и здесь вы можете либо создать защитную оболочку, которую нельзя изменить, либо убедиться, что все соответствующие методы задокументированы правильно.

2 голосов
/ 21 февраля 2011

Если вы не хотите изменять исходный список, не меняйте его.

Вместо этого вы можете изменить копию.

List f(List l){
  l = new ArrayList(l); // the original will not be changed now.
  l.add(new Object());
  return l;
}
2 голосов
/ 21 февраля 2011

Использовать неизменяемый список :

log.info(l.count());
f(Collections.unmodifiableList(list));
log.info(l.count());

Если вы попытаетесь изменить список в методе, вы получите UnsupportedOperationException.

0 голосов
/ 22 февраля 2011

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

/**
 * an immutable wrapper around a list with an added element at the end.
 */
class ImmutableListWrapper<E> extends AbstractList<E> {

    private final List<E> delegate;
    private final E lastElement;

    public ImmutableListWrapper(List<E> start, E last) {
       this.delegate = start;
       this.lastElement = last;
    }


    public E get(int index) {
       if(index == delegate.size()) {
           return lastElement;
       }
       return delegate.get(index);
    }

    public int size() {
        return delegate.size() + 1;
    }
}

public List<Object> f(List<Object> l) {
    return new ImmutableListWrapper<Object>(l, new Object());
}

Если первоначальный список изменяется, новый список тоже изменяется, это по замыслу.

Если ваш исходный список является списком без случайного доступа, то вам лучше унаследовать от AbstractSequentialList и реализовать делегирующий ListIterator вместо get-метода.

0 голосов
/ 21 февраля 2011

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

Если вы хотите, чтобы в ваш список не было внесено никаких изменений, несмотря на то, что спецификация говорит, что попытается это сделать (при условии, что вы не написали сам метод), оберните его как Collections.unmodifiableList(l); и обработайте брошенные исключения, как предлагали другие.

Если вы находитесь на другой стороне - пишете метод и хотите убедиться, что вы не измените содержимое списка - просто не пишите какие-либо модифицирующие утверждения и обязательно упомяните что в спецификации.

...