I want to implement a immutable class that is Iterable in Java. I do it the following way:
public final class MyIterable implements Iterable<Integer> {
private final Collection<Integer> items;
public MyIterable(Collection<Integer> items) {
this.items = Collections.unmodifiableCollection(new LinkedList<Integer>(items));
}
@Override
public Iterator<Integer> iterator() {
return items.iterator();
}
}
This is all good, I cannot change anything in my instances. However, my class still exposes an API that indicates that its internals could be modified through the remove() method on the iterator:
MyIterable myIterable = new MyIterable(Arrays.asList(1, 2, 3));
for (Iterator<Integer> it = myIterable.iterator(); it.hasNext();) {
it.remove(); // I do not want to expose this even
// though I know it will throw an error at runtime.
it.next();
}
Is there some way that I can avoid exposing this remove method and hence have my class exposing a truly immutable API? Ideal would be implementing something like ReadOnlyIterable instead, but something like that does not seem to be available in Java.
It would seem to indicate it by virtue of its method signatures, but those methods are specifically documented to say “No, we do not supply those behaviors!”
In order to carry this through, you would have to also document that you follow identical behaviors to
UnmodifiableList.It is the responsibility of developers to know what the libraries they use do, and the responsibility of library creators to make that information available.
Bottom line is,
Iterableis baked into the language (thefor(a:b)loop) and while you could create your own interface that isReadOnlyIterableit simply won’t be as robust.The problem with this approach is that you sacrifice compile-time integrity. In other words, someone can compile code that uses the
remove()method, and they won’t find out until runtime that it doesn’t work. This means if they happen to not test properly, you won’t find out until it’s in production that the method shouldn’t be used.You could perhaps use annotations and warnings to mitigate this – if they listen to warnings, or use an IDE that tells them about warnings, they’ll find out much earlier. But you can’t rely on users to do the right thing. That’s why you have to throw an exception instead of doing nothing.