Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java - Behavior of Class Members of Generic Classes

Tags:

java

generics

If you declare an instance of a generic class as a raw type in Java, does the compiler assume a parametrized type of Object for all class member methods? Does this extend even to those methods which return some form (e.g. a Collection) of a concrete parametrized type?

I erroneously declared an instance of a generic class without a parametrized type, which led to very interesting downstream effects. I've provided a simplified example of a scenario which produced an 'incompatible types' compilation error. My basic question: Why does javac produce an 'incompatible types' error, complaining about the indicated line below?

import java.util.*;

public class Main {
    public static void main(String[] args) {
       final Test<String> t1 = new Test<String>(); 
       final Test t2 = new Test<String>();

       for (final Integer i : t1.getInts()) {
           System.out.println(i);
       }
       for (final Integer i : t2.getInts()) { //<-- compile-time error
           System.out.println(i);
       }
    }

    public static class Test<T> {
        public Test() {
        }

        public Set<Integer> getInts() {
            final Set<Integer> outSet = new HashSet<Integer>();
            outSet.add(new Integer(1));
            outSet.add(new Integer(2));
            outSet.add(new Integer(3));
            return outSet;
        }
    }
}

An interesting note is that if t2 is declared with a wildcard type (Test<?> t2…) the code compiles and runs as expected.

The use of the for-each loop invokes next() on the iterator on the set returned by getInts() (which returns a generic type gleaned from the Iterable being iterated over). The documentation here mentions that if the Iterable is raw, Objectis used as the return type. Why does the compiler also seem to change the return type of Test.getInts() from Set<Integer> to Set<Object>?

like image 516
drew.m Avatar asked Apr 22 '14 16:04

drew.m


People also ask

What is difference between class and generic class?

The generic class works with multiple data types. A normal class works with only one kind of data type.

Do generics prevent type cast errors?

Implementing generics into your code can greatly improve its overall quality by preventing unprecedented runtime errors involving data types and typecasting.

Which character is used for generic type?

The question mark ( ? ) wildcard character can be used to represent an unknown type using generic code. Wildcards can be used with parameters, fields, local variables, and return types.

Can generic class be inherited in Java?

It inherits all members defined by super-class and adds its own, unique elements. These uses extends as a keyword to do so. Sometimes generic class acts like super-class or subclass. In Generic Hierarchy, All sub-classes move up any of the parameter types that are essential by super-class of generic in the hierarchy.


1 Answers

Because you used a raw Test, all generics in the Test class are effectively type-erased, even unrelated generics such as returning Set<Integer>, so that the Set<Integer> returned by getInts becomes just a raw Set. That explains why you get incompatible types, because a raw Set will return Objects, not Integers.

Section 4.8 of the JLS covers raw types:

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.

and

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.

(emphasis mine)

The reason for this behavior is for backwards compatibility with pre-generics Java code, because the pre-generics versions of built-in Java classes are the same things as the raw type versions of the generified built-in classes. That is, a pre-generics Set is the same thing as a raw Set (generics removed).

like image 142
rgettman Avatar answered Oct 02 '22 02:10

rgettman