A method that declares a mutable data structure as a result and returns actually immutable

Recently, I have a heated discussion on this issue.

Suppose I created this method in Java:

public Set<String> getRich() { return ImmutableSet<String> ....; } 

Whenever I see this in a stretch request, I scream and try to explain why this is wrong. By doing this, I misled the consumers of my method on the promise of a kit. This means that they can remove or add items. javac will be happy to compile, but there will be a RuntimeException . Add to this that he violates the Liskov Principle of Replacement .

Personally, I always do:

 public ImmutableSet<String> getRich() { return ImmutableSet<String> ....; } 

Thus, no one is going to shoot himself in the leg.

One suggested approach is Iterable Return . I think this is bad, because the consumer of this method will lose the potential power of HashSet, HashMap or something else (cannot call set.get (hash)).

Another approach - as a consumer - is to create a copy of the output of the getRich () method. But how will you be sure that consumers will do it?

And, of course, you have people who will adhere to the principles of OOP design and say: "Always program interfaces." For me - in this particular case - this is the worst option.

How would you handle this?

(Off topic: this case is a great example of how static typing can provide validity, but will never guarantee legitimacy).

+5
source share
1 answer

Technically, declaring a return type as Set<T> not a violation of the LSP.

The documentation for Set<T>.Add explicitly states that implementations may raise an UnsupportedOperationException . This means that it is the responsibility of customers to check whether the set is mutable.

Now, whether creating an ImmutableSet<T> extend Set<T> , was a good choice that was controversial. I personally think that this is a very bad choice - client code should not be littered with attempts / catches to find out if the set is mutable. In addition, Set<T> does not even provide the isMutable() method, which allows clients to easily validate! Having a separate interface for immutable collections will be much cleaner.

I personally get around this design flaw and, as you said, declare the return type as ImmutableSet<T> .

The second part of your question is whether you should return an ImmutableSet<T> or Iterable<T> : it really depends on what you think clients will expect from your API. What to consider:

  • Perhaps your implementation will change? Do you think you'll ever need to change your business logic and get another collection back? If yes, go to Iterable<T> ;
  • Are your clients explicitly expecting an ImmutableSet<T> ? If not, go to Iterable<T> ;
  • Otherwise, you can adhere to the principle of "Reliability" , which reads: "Contravariance in the type of input and covariance in the type of output." In other words, select the most abstract type possible for your argument types and the most specific type possible for your return types.
+6
source

Source: https://habr.com/ru/post/1200818/


All Articles