Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Odd compiling error with generic classes and lists

So I have came across a weird compiling error when using a generic class that has a List (or Map or Set, etc) as an attribute.

The compiling error occurs while trying to iterate (using a foreach) the List:

Sample.java:11: error: incompatible types
        for (String string : s.getStringList()) {
  required: String
  found:    Object

Just to be clear, I know there's a simple workaround for this problem, but I want to understand what is wrong with the code

Following is the sample I have created:

import java.util.List;

public class Sample<T> {

    public List<String> stringList;

    public static void main(String[] args) {
        Sample s = new Sample();

        // Why this doesn't work?
        for (String string : s.getStringList()) {

        }

        // Why does both of the following work?
        List<String> newList = s.getStringList();

        Sample<Object> s2 = new Sample<>();
        for (String string : s2.getStringList()) {

        }

    }

    public List<String> getStringList() {
        return stringList;
    }

}
like image 663
Jouwee Avatar asked Mar 13 '14 22:03

Jouwee


2 Answers

These lines

Sample s = new Sample();

// Why this doesn't work?
for (String string : s.getStringList()) {

}

do not work because you are using the raw form of the Sample class. When you use the raw form of the class, all generics in the class, even unrelated generics, have type erasure performed. That means that getStringList now just returns a List of Objects, not a List<String> of Strings.

This part of Java was introduced with Generics in Java 1.5 so that the old version of classes that now use generics would be backwards compatible. That way, something that iterates over List, which had to use Object before, still can use Object by using the raw form of List.

The JLS, Section 4.8 deals with raw types:

More precisely, a raw type is defined to be one of:

  • The reference type that is formed by taking the name of a generic type declaration without an accompanying type argument list.

and

The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.

(emphasis mine)

The reasoning:

The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of generics into the Java programming language is strongly discouraged. It is possible that future versions of the Java programming language will disallow the use of raw types.

like image 149
rgettman Avatar answered Nov 19 '22 19:11

rgettman


You're using the raw Sample type, and the compiler thus assumes that you don't care about generic types, even for variables not declared as raw types inside this object. It's a strange rule, but it's how it is. You shouldn't use raw types if you care about generics. As soon as you use a raw type, all its members are also taken as raw types by the compiler.

like image 37
JB Nizet Avatar answered Nov 19 '22 18:11

JB Nizet