5

Let's say I create a Collection in an instance method. I do not assign that reference to any instance variables. Rather, I just return it to the invoker. Then, I exit.

Now, the only thing with a reference to that Collection is the invoker.

So, as the method writer, why do I care what the invoker does with the returned Collection? Whether the invoker wants to treat it as Unmodifiable / Immutable is none of my business. In fact, I'd be breaking my method scope by hand-cuffing the invoker like that.

Right now, I'm writing a method that tokenizes a String by using regular expressions. I was ready to go with returning an ImmutableList. But, I'm about to change my mind.:

public interface someInterface {
    List<String> tokenize(String text);
    com.google.common.collect.ImmutableList<String> tokenizeImmutable(String text);
}

Once I exit and die, whether the invoker mutates my Collection, the one that I created, is nothing that I should have any influence over. I mean, I could lock it down, and conceptually the tokenization should be locked down, but that is out of my scope.

3
  • 3
    I think you're right in this scenario, it's more important when you return a reference to a member which you don't expect to be changed – Bwmat Feb 12 '16 at 3:09
  • 1
    You could return the same instance to multiple callers if it's immutable. – CodesInChaos Feb 12 '16 at 8:54
  • 1
    The mutability/immutability issue is a red herring. The real issue here is that the caller has no choice regarding what kind of list you're returning. Even if you return a mutable list, there's multiple possible implementations and no hard-coded choice will satisfy every caller. But for whatever it's worth immutable is a better default. – Doval Feb 12 '16 at 16:10
1

Short (but useless) answer to your question: No, it does not in general break the "scope".

As with everything else, there are cases in which forcing immutability is good and cases in which it isn't.

Some cases are blacker or whiter than others of course. Your example is a candidate for when you might not want to force this on the invoker. However, you may be returning a Collection and while only created locally, you may have a case where this collection is a special-purpose hand-built beast. And that beast just happens to have abysmal performance when you modify it, so you may want to protect your users from having to know this fact by simply prohibiting modifications.

As @Bwmat pointed out, another typical scenario is providing access to a member attribute collection. Normally, you do not want that to change outside of your class, so you lean towards immutability. But that's no silver bullet either. Sometimes your callers run in a different thread and simply iterating over that collection may cause a ConcurrentModificationException, so you decide to return a copy instead. In even rarer cases you may even feel compelled to return the actual collection (though that is pretty darn dark area now).

As you can see from these examples, the discussion often includes both sides' behaviors - the method declarator's side and the client's side. The very term method scope used in your question is ambiguous and that may be at the root of the issue here.

We have the strictly lexical method scope as used in the compiler domain. It's the part of your code in which those arguments and local variables of the method are accessible. Obviously, this scope is not meant here, since looking at the method caller is clearly outside of it.

The method scope of an interface method though reaches far beyond that to all points at which the class loader can potentially access the interface. In other words, it is part of that scope to consider invoker usages of your method and its return values.

Finally, you may define an entirely different scope for your method in the area of your problem domain (as in the "string tokenization scope") and you may come to yet another solution on what the best return type should be.

In summary, be aware of what your method's scope actually means to you and your team and as usual, weigh the pros and cons of each approach within that scope.

0
4

Returning immutable data is an example of "the pit of success". In order for some other part of the system to modify it, it must explicitly convert it to a mutable data set and modify it. This reduces the chances of an accidental modification leading to a bug. Thus the phase: you are making it easier to write reliable code by using immutable data types by default, you are making it easier to fall into the "pit of success".

The only reason not to therefore return an immutable data set is for performance reasons. Even here, there is a risk of micro-optimising the code. If the performance of the method becomes a problem, then consider changing it; not before.

1

First, returning an immutable list doesn't preclude the invoker from just immediately making a mutable list out of it, so, other than some performance concerns, you are not constraining what's possible for the invoker. However, you touched on why it is still desirable to return an immutable collection in your question.

I do not assign that reference to any instance variables. Rather, I just return it to the invoker.

How does the invoker know this? If I'm accessing your tokenizer through someInterface how are you going to enforce this in every class that implements that interface? A comment? If you were programming in Rust this would be enforceable, but few other languages allow you to express transferring ownership.

By returning an immutable collection, the invoker immediately knows that they don't have to worry about someone somewhere having a reference to the same list; they immediately know that two calls to the tokenizer return non-interacting objects.

If the performance cost of returning an immutable collection was too great though, you could consider using techniques like Rust and C++ to return an object representing having unique ownership of a value. This would allow you to communicate (though not quite enforce) the guarantee. Admittedly, without the guarantee it probably isn't worth the hassle.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.