在java方法调用中保持参数不变

时间:2011-02-21 17:20:36

标签: java const-correctness mutability

如果我想调用这样的方法:

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

一切都很好,除非我调用该方法,它实际上修改了它的参数,无论如何都在那附近?

// suppose l is instantiated at this point
log.info(l.count());// prints 0
f(l);
log.info(l.count());// prints 1

无论如何都要声明f以保持l在java中不变?

我知道我可以在l上执行深度克隆并传递它,但是在l非常大的情况下,此操作很昂贵。

5 个答案:

答案 0 :(得分:6)

好吧,不要调用会修改它的方法。如果没有复制,你会期望这样的方法做什么?它要么必须表现不同(例如,在调用add时没有做任何事情)或抛出异常。你可以通过将它包装在一个不可修改的列表中来使它抛出异常...但是如果方法的目的是更改集合,你可能想要抛出异常..

我知道这听起来像是一个陈腐的答案,但我希望它真正触及你需要思考的内容:如果你有一个不应修改的集合,你想要调用一个尝试修改集合的方法,您应该考虑为什么要首先调用该方法。

我确实理解困难的部分是知道哪些方法修改集合 - 这就是你可以防御性地创建一个不可修改的包装器,或者确保正确记录所有相关方法。< / p>

答案 1 :(得分:2)

使用unmodifiable list

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

如果您尝试修改方法中的列表,您将获得UnsupportedOperationException

答案 2 :(得分:2)

如果您不想更改原始列表,请不要更改它。

您可以改为复制。

List f(List l){
  l = new ArrayList(l); // the original will not be changed now.
  l.add(new Object());
  return l;
}

答案 3 :(得分:0)

这就是为什么你应该总是从编写规范开始,并注意提供给你的有关API的规范。这将列在方法的规范中。

如果您想强制不对列表进行任何更改,请忽略规范说它会尝试这样做(假设您没有自己编写方法),请将其作为Collections.unmodifiableList(l);进行包装。并像其他人建议的那样处理抛出的异常。

如果您在另一边 - 编写方法并且您希望确保不更改列表的内容 - 只需编写任何修改语句并确保在规范中提到。

答案 4 :(得分:0)

如果您知道原始列表本身没有变化,并且想要一个包含原始列表的所有内容的新List以及另外一个新元素,您可以考虑使用两者的包装器,如下所示:

/**
 * 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-method。