Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the Java Compiler complain on using foreach with a raw type? [duplicate]

I got a strange compiler error when using generics within a for-each loop in Java. Is this a Java compiler bug, or am I really missing something here?

Here is my whole class:

public class Generics<T extends Object> {
  public Generics(T myObject){
    // I didn't really need myObject
  }

  public List<String> getList(){
    List<String> list = new ArrayList<String>();
    list.add("w00t StackOverflow");
    return list;
  }

  public static void main(String...a){
    Generics generics = new Generics(new Object());
    for(String s : generics.getList()){
      System.out.println(s);
    }
  }
}

The compiler is complaining about the line with the for-each: "Type mismatch cannot convert from element type Object to String."
If I make this subtle change, it compiles:

public static void main(String...a){
  Generics<?> generics = new Generics(new Object());
  for(String s : generics.getList()){
    System.out.println(s);
  }
}

I know getList() does use generics, but it uses them in what I thought was a completely unrelated way. I could understand this if I were trying to iterate over something of type T and getList() returned a List<T> or something, but that's not the case here. The return type of getList() should have absolutely nothing to do with T and shouldn't care whether I use the raw type for my Generics object or not...right? Shouldn't these be completely unrelated, or am I really missing something here?

Note that the code also compiles if I do this, which I thought should have been equivalent to the first as well:

public static void main(String...a){
  Generics generics = new Generics(new Object());
  List<String> list = generics.getList();
  for(String s : list){
    System.out.println(s);
  }
}
like image 835
Michael McGowan Avatar asked Mar 25 '11 18:03

Michael McGowan


2 Answers

The difference is that when you use the raw type, all the generic references within the member signatures are converted to their raw forms too. So effectively you're calling a method which now has a signature like this:

List getList()

Now as for why your final version compiles - although it does, there's a warning if you use -Xlint:

Generics.java:16: warning: [unchecked] unchecked conversion
    List<String> list = generics.getList();
                                        ^

This is similar to:

 List list = new ArrayList();
 List<String> strings = list;

... which also compiles, but with a warning under -Xlint.

The moral of the story: don't use raw types!

like image 80
Jon Skeet Avatar answered Oct 19 '22 18:10

Jon Skeet


Change the line

Generics generics = new Generics(new Object());

to

Generics<?> generics = new Generics<Object>(new Object());

The root of your problem is that you are using a raw type so the type of the getList method is List, not List<String>.

like image 44
Mike Samuel Avatar answered Oct 19 '22 19:10

Mike Samuel