开发者

keeping an argument unchanged in java mehod call

if I want to call a method like this:

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

All is fine except if I call the method, it actually modifies its argument, is there anyway around that?开发者_如何学编程

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

is there anyway to declare f in a way to keep l unchanged in java?

I know that I can perform a deep clone on l and pass it, but in cases where l is really big, this operation is expensive.


Well, don't call a method which would modify it. What would you expect such a method to do, without copying? It would either have to behave differently (e.g. not doing anything when add is called) or throw an exception. You can make it throw an exception by wrapping it in an unmodifiable list... but if the purpose of the method is to change the collection, you probably don't want an exception to be thrown...

I know this sounds like a bit of a trite answer, but I hope it really gets to the heart of what you need to think about: if you have a collection which shouldn't be modified, and you want to call a method which tries to modify a collection, you should be considering why you want to call that method in the first place.

I do understand that the difficult part is knowing which methods will modify the collection - and that's where you can either defensively create an unmodifiable wrapper, or make sure all the relevant methods are documented correctly.


Use an unmodifiable list:

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

If you try to modify the list within the method, you will get an UnsupportedOperationException.


If you don't want to change the original list, don't change it.

You can instead change a copy.

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


This is why you should always start with writing a specification and take good notice of the one provided to you about APIs. This would be listed in the specification of the method.

If you want to force that no changes are made to your list, disregarding that the spec says it will attempt to do so (assuming you did not write the method itself), wrap it up with as an Collections.unmodifiableList(l); and handle the thrown exceptions, as the others suggested.

If you are on the other side - writing the method and you want to make sure you don't change the contents of the list - just don't write any modifying statements and make sure to mention that in the spec.


If you know that your original list does not change by itself, and want a new List which contains all contents of the original list and additionally your one new element, you could consider using a wrapper around both, like this:

/**
 * 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());
}

If the original list changes, the new list changes too, this is by design.

If your original list is a no-random-access list, then you would better inherit from AbstractSequentialList and implement a delegating ListIterator instead of a get-method.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜