6 Replies Latest reply: May 22, 2012 10:59 PM by 815841 RSS

    Why doesn't Iterable's iterator() allow extends?

    815841
      I'm curious if there is a good reason why the iterator method in Iterable<T> doesn't look like:

      Iterator<? extends T>     iterator();

      It seems like the above would be more general, and would allow for interfaces extending Iterable<T> to be in turn extended in more interesting ways. I can't see a drawback. But, there must be one right?
        • 1. Re: Why doesn't Iterable's iterator() allow extends?
          EJP
          Same reason that get() returns a T.

          Edited by: EJP on 23/05/2012 11:49
          • 2. Re: Why doesn't Iterable's iterator() allow extends?
            815841
            Ok, it seems that the Java API doesn't do it because it is incorrect, but doesn't do it because it would make the code that calls the method more painful to write. I.e. you would need to call it with:

            Iterator<? extends Something> it=iterable.iterator();

            And having wildcards in the code of a calling class is not desirable. I found this out from the free chapter of Effective Java Second Edition, although I'm not entirely satisfied with their explanation of why it should be avoided. The above code is a little hard to read, but it is not that bad and there might be instances where it would be useful.

            ---

            EJP, I appreciate your answer, but I don' think your explanation works. The objects in Collection<T> can be anything that extends T not just things which are precisely T.

            Here's a contrived example which compiles fine with my version of Iterable.

            public interface MyIterable<T> {
            Iterator<? extends T> iterator();
            }

            public interface A extends MyIterable<Object> {}

            public interface B extends A {
            @Override
            Iterator<? extends String> iterator(); // even this works here: Iterator<String> iterator();
            }

            I'd assume of course that there would be extra stuff in the interfaces. If you have an object of type A, you iterate over objects. But, if you have an object of type B, then you can iterate over something more specific like a string.

            Is there a reason to do or not to do this? Is there a better way to do what I've done above?
            • 3. Re: Why doesn't Iterable's iterator() allow extends?
              EJP
              See edit. I will just say that the reason is the same reason that get() returns T rather than <? extends T>, whatever the syntax for that would be.
              • 4. Re: Why doesn't Iterable's iterator() allow extends?
                815841
                The way I see it, there is only notation in Java for a function to return T or something that extends T. There is no notation that guarantees that the object returned is precisely T (and not a sub-type). For instance, if I have an List<Object>, I can put a String a string in it. When I use get() it will return the String cast as an Object.

                Here is a contrasting example to prove my point.

                public interface MyCollection<T> {
                T get(int i);
                }

                public interface C extends MyCollection<Object> {}

                public interface D extends C {
                @Override
                String get(int i);
                }

                This code I take it is acceptable. It seems pretty closely related to the previous code I posted.
                • 5. Re: Why doesn't Iterable's iterator() allow extends?
                  815841
                  I've decided that this example highlights a deficiency in Java's implementation of generics. Namely, Iterator<String> should be a sub-type of Iterator<Object> but isn't. If you avoid using generics you can implement my A B example easily:

                  public interface ObjectIterator {
                  Object next();
                  }

                  public interface StringIterator extends ObjectIterator {
                  @Override
                  String next();
                  }

                  public interface A2 {
                  ObjectIterator iterator();
                  }

                  public interface B2 extends A2 {
                  @Override
                  StringIterator iterator();
                  }

                  This version is better in the sense that it avoids the wildcards. However, it is frustrating to have to duplicate the Iterator interface. Note that you can't make ObjectIterator extend Iterator<Object> and StringIterator extend Iterator<String>, because StringIterator would inherit from two versions of Iterator<T>. I'm still curious if there is a better way to do these things.
                  • 6. Re: Why doesn't Iterable's iterator() allow extends?
                    815841
                    One more comment. I found the following discussion of this bug:

                    http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5060257

                    I guess this means that even some people at Sun considered this a bug.