2

In order to achieve better immutability, should my API return java.lang.Iterable<T> or java.util.Enumeration<E>? Since Iterable's iterator has a remove() method, one could say Enumeration is the right choice, but the javadoc says "New implementations should consider using Iterator in preference to Enumeration".

I'm aware about unmodifiable collections, but in that case we still end up with collections that expose add and remove methods.

Any other options in Java?

5
  • would T.toArray() fit your parameters? An array T[] tArray has no methods. Is it the collection itself you want immutable or the objects referred to by the collection? – scorpdaddy May 17 '16 at 14:29
  • @scorpdaddy It is the collection itself. Primitive array could be an option if the client couldn't replace or remove elements from the structure, which is not true. One can ultimately do tArray[i] = new Object(). – juliaaano May 17 '16 at 20:24
  • Iterable has a much better integration than enumeration and Arrays (for each loop etc.), – pdem May 20 '16 at 7:19
  • Iterable<T> doesn't have a remove() method. Iterator<E> does. – Tulains Córdova Aug 15 '16 at 13:32
  • @TulainsCórdova Thanks! I've updated the question. – juliaaano Sep 16 '16 at 9:07
3

As Brian says (and your reference to the docs), Iterables are much more common and what programmers are used to. Thus, use Iterable - it also allows to use the "foreach" statement, which does not support Enumeration.

As the unmodifiable collections are part of the base JDK, I would say that add/remove methods in the collection, and remove methods in the iterator, are exposed is just how it is in Java. It is instead common to simply document the API sufficiently, and throw UnsupportedOperationException where the user violates the API.

I would suggest the implementation of Tulains. However, throw an UnsupportedOperationException in remove instead of doing nothing. This communicates the interface correctly: you return an unmodifiable collection. This is also the behaviour of standard JDK classes (for instance emptySet() and unmodifiableList() from the Collections class).

For completeness, the implementation (with sufficient rep, I would have just commented on Tulains answer):

/* This is unmodifiable */
public class MyList<T> implements Iterable<T> {

    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            @Override
            public boolean hasNext() {
                return false; // TODO: to implement
            }

            @Override
            public T next() {
                return null; // TODO: to implement
            }

            @Override
            public void remove() {  
                throw new UnsupportedOperationException();
            }
        };
    }

}

Note that I also moved the Iterator to an inner class to separate the collection and its iterator. This is also how it is done, for instance, for the class Collections.UnmodifiableList.

2

Iterable<T> doesn't have a remove() method. Iterator<E> does.

If you return an Enumeration the client would have to iterate it like this:

while(list.hasMoreElements()){
    e=nextElement();
}

If you want client code to be able to iterate it like this:

for(Element e; list){
    //do something with e
}

and keep it inmmutable at the same time, you should extend Iterator and Iterable, make remove() throw IllegalStateException if called, and return an object of that type:

public class MyList<T> implements Iterable<T>, Iterator<T> {

    @Override
    public Iterator<T> iterator() {
        return this;
    }

    @Override
    public boolean hasNext() {
        return false; // to implement
    }

    @Override
    public T next() {
        return null; // to implement
    }

    @Override
    public void remove() {  
      throw new IllegalStateException("I'm inmutable");
    }

    public static void main(String[] args) {
        MyList<String> l = new MyList<String>();
        for (String s: l){
            System.out.println(s);
        }
    }

}

You could return an object of type MyList.

3
  • As noted in the other answers, remove() should throw an exception, not do nothing. Otherwise a good answer. – user949300 Sep 14 '16 at 16:01
  • @user949300 You are right. I changed the answer. – Tulains Córdova Sep 14 '16 at 16:10
  • Thanks! I've updated the question to explain the Iterable's iterator has the remove() method. – juliaaano Sep 16 '16 at 9:09
1

I would normally expect an Iterable. It's a member of the Collections framework, and as such returning an Iterable will allow you to play nicely with other components.

I take your point re. immutability, though. There isn't a particularly nice means of returning a read-only collection. You could use Collections.unmodifiableList() but that won't provide a compile time check. The same applies to buidling a defensive copy.

Returning a Collection<? extends ElementType> if you're at liberty to return a collection rather than an iterator, will give you the compile-time safety. There's some more related discussion here

4
  • Well, the wildcard-approach only prevents adding and setting elements, but not removing if I recall correctly. And it does not clearly communicate the intent of being immutable which is why some static analysis tools flag it as an issue (e.g. Sonar squid:S1452). See also the accepted answer to stackoverflow.com/q/22815023/2513200 – Hulk May 17 '16 at 12:22
  • I certainly agree with you that's not a perfect solution (including the concept of 'intent'). I don't think there's a perfect intuitive solution, unfortunately – Brian Agnew May 17 '16 at 12:26
  • Also, it does not even really prevent adding - you can still add null (admittedly, that's probably a minor issue in most szenarios, but...). – Hulk May 17 '16 at 12:31
  • @BrianAgnew Good insight with Producer Extends approach, but still didn't help much compared to the other options. Thanks. – juliaaano May 17 '16 at 20:29

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.