简体   繁体   中英

Returning a private collection using a getter method in Java

I have a number of Java classes that use private sets or lists internally. I want to be able to return these sets/lists using a get...List() method.

The alternatives I am considering:

  1. return a reference to the internal object
  2. construct a new set/list and fill it up (this seems bad practice?)
  3. use Collections.unmodifiableList(partitions);

Which of these is the most common / best way to solve this issue?

There are many aspects to consider here. As others already have pointed out, the final decision depends on what your intention is, but some general statements regarding the three options:

1. return a reference to the internal object

This may impose problems. You can hardly ever guarantee a consistent state when you are doing this. The caller might obtain the list, and then do nasty things

List<Element> list = object.getList();
list.clear();
list.add(null);
...

Maybe not with a malicious intention but accidentally, because he assumed that it was safe/allowed to do this.


2. construct a new set/list and fill it up (this seems bad practice?)

This is not a "bad practice" in general. In any case, it's by far the safest solution in terms of API design. The only caveat here may be that there might be a performance penalty, depending on several factors. Eg how many elements are contained in the list, and how the returned list is used. Some (questionable?) patterns like this one

for (int i=0; i<object.getList().size(); i++)
{
    Element element = object.getList().get(i);
    ...
}

might become prohibitively expensive (although one could argue whether in this particular case, it was the fault of the user who implemented it like that, the general issue remains valid)


3. use Collections.unmodifiableList(partitions);

This is what I personally use rather often. It's safe in the sense of API design, and involves only a negligible overhead compared to copying the list. However, it's important for the caller to know whether this list may change after he obtained a reference to it.

This leads to...


The most important recommendation:

Document what the method is doing! Don't write a comment like this

/** 
 * Returns the list of elements.
 *
 * @return The list of elements. 
 */
public List<Element> getList() { ... }

Instead, specify what you can make sure about the list. For example

/** 
 * Returns a copy of the list of elements...
 */

or

/** 
 * Returns an unmodifiable view on the list of elements...
 */

Personally, I'm always torn between the two options that one has for this sort of documentation:

  • Make clear what the method is doing and how it may be used
  • Don't expose or overspecify implementation details

So for example, I'm frequently writing documentations like this one:

/** 
 * Returns an unmodifiable view on the list of elements. 
 * Changes in this object will be visible in the returned list. 
 */

The second sentence is a clear and binding statement about the behavior. It's important for the caller to know that. For a concurrent application (and most applications are concurrent in one way or the other), this means that the caller has to assume that the list may change concurrently after he obtained the reference, which may lead to a ConcurrentModificationException when the change happens while he is iterating over the list.

However, such detailed specifications limit the possibilities for changing the implementation afterwards. If you later decide to return a copy of the internal list, then the behavior will change in an incompatible way.

So sometimes I also explicitly specify that the behavior is not specified:

/** 
 * Returns an unmodifiable list of elements. It is unspecified whether 
 * changes in this object will be visible in the returned list. If you
 * want to be informed about changes, you may attach a listener to this 
 * object using this-and-that method...
 */

These questions are mainly imporant when you intent do create a public API. Once you have implemented it in one way or another, people will rely on the behavior in one or the other way.

So coming back to the first point: It always depends on what you want to achieve.

Your decision should be based on one thing (primarily)

  1. Allow other methods to modify the original collection ?

    Yes : return a reference of the internal object.

    No :

    1. construct a new set/list and fill it up (this seems bad practice? -- No. Not at all. This is called Defensive programming and is widely used).
    2. use Collections.unmodifiableList(partitions);
return a reference to the internal object

In this case receiver end can able to modify the object's set or list which might not be requirement. If you allow users to modify state of object then it is simplest approach.


construct a new set/list and fill it up (this seems bad practice?)

This is example shallow copy where collection object will not be modifiable but object would be used same. So any change in object state will effect the actual collection.


use Collections.unmodifiableList(partitions);

In this case it returns an unmodifiable view of the specified list. This method allows modules to provide users with "read-only" access to internal lists. This could be used as best practice in situation where you want to keep object's state safe.

I believe the best solution is to return an unmodifiable list. If compared to the construction of a new list, returning an unmodifiable "proxy" of the original list may save the client from implicitly generating a lot of unnecessary lists. On the other hand, if the client really needs to have a modifiable list, let it create a new list by itself.

The problem you still have to consider is that the objects contained into the list may be modified. There is no cheap and easy const-correctness in Java.

The second option is definitely the right way to go.

The other two options depend on your requirements.

  • If you are not going to modify the list values outside the class, return an unmodifiable list.
  • otherwise, just return the reference.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM