Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are Java Generics an All-or-Nothing Decision? [duplicate]

Tags:

java

generics

I have the following code:

public class Main {
    public static void main(String[] args) {
        Generic generic = new Generic<Integer>(5);
        List<String> stringList = generic.getStringList(); // this line is where the compiler complains
    }
}

public class Generic<T> {
    private T member;

    public Generic(T member) {
        this.member = member;
    }

    public T getMember() {
        return member;
    }

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

Note that class Generic is declared with a generic type parameter, but the variable generic in method main is of the erasure type, i. e. without a type parameter. What I do not understand is why the compiler complains about the line with the assignment of the List<String>:

Warning:(6, 56) java: unchecked conversion
  required: java.util.List<java.lang.String>
  found:    java.util.List

The Method clearly returns a List<String>, independently of the generic parameter of the class. And this is what the variable stringList expects. It seems that not using the generic parameter on class level for variable generic switches off all generics processing, and not just that depending on the type parameter of the class.

I am using the standard Oracle Java 1.7.0_55 compiler, if that matters.

I am not asking how to get rid of the warning. I know I should declare the variable type as Generic<Integer>, or could use @SuppressWarnings("unchecked"). My questions are the following:

Is this behavior documented?

What is the reason for this strange behavior?

like image 575
FrankPl Avatar asked Oct 24 '14 16:10

FrankPl


People also ask

What is the point of generics in Java?

Java Generics helps the programmer to reuse the code for whatever type he/she wishes. For instance, a programmer writes a generic method for sorting an array of objects. Generics allow the programmer to use the same method for Integer arrays, Double arrays, and even String arrays.

What are the advantages of generics in Java?

Code that uses generics has many benefits over non-generic code: Stronger type checks at compile time. A Java compiler applies strong type checking to generic code and issues errors if the code violates type safety. Fixing compile-time errors is easier than fixing runtime errors, which can be difficult to find.

Do generics allow for code reuse?

By making use of generic classes and methods, one can also reuse the code as per one required data type during implementation.


3 Answers

When you use the erasure of a type, it removes all trace of generics - not just the uses of the type parameter T. So your generic variable acts as if it's referring to this type:

// After type erasure
public class Generic {
    private Object member;

    public Generic(Object member) {
        this.member = member;
    }

    public Object getMember() {
        return member;
    }

    public List getStringList() {
        return new ArrayList();
    }
}

This is documented in the JLS - start at section 4.6 and follow the links. It's not as clear as it might be, but it is documented.

The reasoning is that if you're using a raw type, the compiler expects you to not be aware of generics at all - because it's likely to be compiling legacy pre-Java-5 code. That's proved a little unrealistic over time, but I believe it was the motivation for the spec being the way it is.

like image 102
Jon Skeet Avatar answered Oct 19 '22 01:10

Jon Skeet


The answer to your first question is here.

Specifically the lines:

Type erasure also maps the signature (§8.4.2) of a constructor or method to a signature that has no parameterized types or type variables. The erasure of a constructor or method signature s is a signature consisting of the same name as s and the erasures of all the formal parameter types given in s.

The type parameters of a constructor or method (§8.4.4), and the return type (§8.4.5) of a method, also undergo erasure if the constructor or method's signature is erased.

The erasure of the signature of a generic method has no type parameters.

As far as the reason, my answer is somewhat "handwavy." Basically, Java's type system was weaker than it is now, and Generics have exposed some low-level implementation details that cause headaches. Specifically, every time you insert an item into an array, you incur a type check at runtime. Java arrays are covariant. Since a type-check occurs at runtime, and the erasure behavior as defined in the spec, the compiler wants to warn you that you should be aware of possible issues down the line.

So more or less, its a design decision that caused lots of headaches after Generics were implemented.

like image 40
avgvstvs Avatar answered Oct 19 '22 01:10

avgvstvs


In addition to Jon's answer, what you perhaps want to do is to declare your variable to still use generics, but accept any type:

Generic<?> generic = ...;

Leaving out the type modifier completely will, as Jon explained, also disable other generics declarations not directly related to the class' type.

like image 3
jarnbjo Avatar answered Oct 19 '22 03:10

jarnbjo