1

I have a class Super and class Sub where Sub extends Super.

I'm trying to write a class which contains a list of Sub but must implement Iterable<Super>. What I tried to to is:

class Bag implements Iterable<Super> {
     List<Sub> list;

     public Iterator<Super> iterator() {
          return list.iterator();
     }
 }

This doesn't compile (the error message is Cannot cast from Iterator<Sub> to Iterator<Super>). I tried casting in several places, and wildcards in other places, but I don't see what's the problem. I certainly don't see why

public Iterator<Super> iterator() {
    return (Iterator<Super>)list.iterator();
}

wouldn't cast. It's possible that java iterator/iterable subinterface has the solution, but I don't understand it.

///// UPDATE

I managed to do it this way:

Iterator<? extends Super> iterator = ((List<? extends Super>)list).iterator();
return (Iterator<Super>)iterator;

(which gives a warning, Type safety: Unchecked cast from Iterator<capture#4-of ? extends Super> to Iterator<Super>. I can live with it, but why does it give this warning?)

Community
  • 1
  • 1
ModdyFire
  • 706
  • 3
  • 9
  • 19

2 Answers2

2

If you're using java 8 an easy way to do this is:

class Bag implements Iterable<Super> {
    List<Sub> list;

    public Iterator<Super> iterator() {
        return list.stream().map(s -> (Super)s).iterator();
    }
}
Alex - GlassEditor.com
  • 14,957
  • 5
  • 49
  • 49
0

I certainly don't see why

List<Sub> list;

public Iterator<Super> iterator() {
    return (Iterator<Super>)sections.iterator();
}

wouldn't cast.

Consider situation where you have classes Fruit Apple Banana. You have list of Fruits

List<Fruit> list = new ArrayList<>();

so you can add to it any kind of fruit like Apple or Banana.

list.add(new Apple());
list.add(new Banana());

Now would it be safe to cast such list to more specified type, lets say list of Apples? No. Why? Consider following example:

List<Apple> apples = (List<Apple>)list; //lets assume for now that this is OK
for (Apple ap : apples){
    //do something with apple
}

But list contains not only Apples but also Banana, which is not right argument for code in for loop.

You to solve this problem in many ways, for instance you can try using wildcards like List<? extends Super>. It all depends on what you are actually trying to achieve.

Response to your update

List<Sub> list;

public Iterator<Super> iterator() {
    Iterator<? extends Super> iterator = ((List<? extends Super>)list).iterator();
    return (Iterator<Super>)iterator;
}

Is same as

public Iterator<Super> iterator() {
    List<? extends Super> tmp = list;                     //1
    Iterator<? extends Super> iterator = tmp.iterator();  //2
    return (Iterator<Super>) iterator;                    //3
}

In line marked //1 you are creating temporary reference of type List<? extends Super> and use it to handle list which is type List<Sub> which is perfectly fine, because <? extends Super> was meant to hold type which extends Super like in this case Sub.

In line //2 you are creating iterator using tmp reference so you are getting iterator with same generic type as this reference so it is Iterator<? extends Super>.

Now problem can happen here

return (Iterator<Super>) iterator;                    //3

Consider situation:
You have List<? extends Super> and you want to cast it to List<Super>. Would that be safe? Not really, because via List<Super> you would be able to add any instance of Super type and its subtypes, like SubFoo or SubBar. It means that if List<? extends Super> would hold List<SubBar> you would be able to add to this list not only desired SubBar but also SubFoo, which is potentially dangerous. That is why compiler will inform you about potential danger.

Interestingly this time you will not see compilation error, but warning. It is probably because explicitly casting List<Sub> to List<Super> is to big shortcut which could lead to misunderstandings because programmers could forget what wrong could happen.

Using casting from wildcard may demonstrate that you understand generics enough to say "OK compiler, I am aware of possible problems you want to protect me from, but I know what I am doing", that is why you are seeing warning instead of compilation error.

Pshemo
  • 122,468
  • 25
  • 185
  • 269
  • But I _know_ it is a list of apples. I just want to tell the compiler it is. – ModdyFire May 29 '14 at 01:47
  • @ModdyFire You know, but compiler doesn't, so it assumes that since there is risk such code shouldn't be allowed. – Pshemo May 29 '14 at 01:49
  • Also, it lets me cast the list (see first line) but wouldn't let me cast the iterator. I have an iterator of apples. I want to iterate over fruit - there cannot be an issue. – ModdyFire May 29 '14 at 01:50