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

Posted

技术标签:

【中文标题】在java方法调用中保持参数不变【英文标题】:keeping an argument unchanged in java mehod call 【发布时间】:2011-02-21 17:20:35 【问题描述】:

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

  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 以在 java 中保持 l 不变?

我知道我可以对 l 执行深度克隆并通过它,但是在 l 真的很大的情况下,这个操作很昂贵。

【问题讨论】:

【参考方案1】:

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

我知道这听起来有点陈词滥调,但我希望它真正触及您需要考虑的核心:如果您有一个不应该修改的集合,并且您想调用尝试修改集合的方法,您应该首先考虑为什么要调用该方法。

我确实知道,困难的部分是知道哪些方法修改集合 - 这就是您可以防御性地创建不可修改的包装器的地方,或者确保所有相关方法都被正确记录。

【讨论】:

+1 - 您可以进行修改,也可以进行新的修改。您不能同时更改和未更改对象。 我不知道我在做什么的原因是我正在针对一个接口进行编码。我的图书馆的用户将提供实施。我一直在寻找类似 C++ 中的 const 关键字的东西,您可以在其中强制我的库的使用者不要调用任何在我的参数中未标记为 const 的方法。所以如果他这样做了,他会得到一个编译时错误。更好的选择是让他获取我列表的易失副本,然后他可以随心所欲地使用它。在我的用例中,我有一个回调列表,我想将我的列表传递给所有回调,并且它们都不应该更改列表。 综上所述,我认为传递一个不可修改的列表是一种选择,不幸的是你在运行时得到异常而不是编译时错误。如果我有另一个对象,我将不得不以类似的方式包装它。不太理想,但据我所知和您的回答,没有更好的选择。 @Charbel:听起来你的界面应该是 ImmutableList(来自 Guava)或类似的东西。但从根本上说,我希望一个操作是否应该修改集合是相当明显的。哎呀,即使类型系统没有说清楚,接口文档也应该。【参考方案2】:

使用unmodifiable list:

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

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

【讨论】:

这将导致 f() 失败,而不是产生将产生所需结果的工作 f()。【参考方案3】:

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

您可以改为更改副本。

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

【讨论】:

【参考方案4】:

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

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

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

【讨论】:

【参考方案5】:

如果您知道您的原始列表不会自行更改,并且想要一个包含原始列表的所有内容以及一个新元素的新列表,您可以考虑在两者周围使用包装器,如下所示:

/**
 * 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 方法。

【讨论】:

以上是关于在java方法调用中保持参数不变的主要内容,如果未能解决你的问题,请参考以下文章

使用 Mockito 多次调用具有相同参数的相同方法

使用 Mockito 多次调用具有相同参数的相同方法

java重定向时如何保持地址栏不变

多态理解

在vb中传递参数的方法有几种 带你了解最常见的2种方法

页面调用数据,并且刷新的时候,如何保持你的查看数据的位置不变,而不会跳转到开始位置