Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a Java generic raw class erase all generics to object when type parameters are unspecified? [duplicate]

Tags:

If I have a class:

public class GenericClass<TBlah extends Number> {     public List<String> getList() {         return null;     } } 

When I attempt to use that method from another class:

public class OtherClass {     public void test() {         GenericClass a = null;         for (String s : a.getList()) {          }     } } 

Why does a.getList() return a List<Object> until I change the line above the for loop to:

GenericClass<Number> a = null; 

At which point a.getList() returns a List<String> like it should do?

Edit: I don't understand why the contract specified by getList() should be affected whatsoever by how I declare my variable 'a'. getList() always returns a List<String>, it doesn't matter what TBlah is.

like image 887
Xenoprimate Avatar asked Jul 19 '13 10:07

Xenoprimate


People also ask

Why are raw types bad in Java?

The warning shows that raw types bypass generic type checks, deferring the catch of unsafe code to runtime. Therefore, you should avoid using raw types. The Type Erasure section has more information on how the Java compiler uses raw types.

How generics works in Java What is type erasure?

Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to: Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded.

Is a raw type should be parameterized?

In summary, raw types should NEVER be used in new code. You should always use parameterized types.

Can generics take multiple type parameters?

Multiple parametersYou can also use more than one type parameter in generics in Java, you just need to pass specify another type parameter in the angle brackets separated by comma.


1 Answers

Because this is how generics work. Do not forget that before generics when you declared a List it was a list of Object. You were expected to put/get Object and you were forced to cast to get your object with the correct type. Actually it still is a list of Object in runtime.

Generics is a way for the compiler to guarentee you type safety at compile time assuming you have no warnings. In runtime there are no List<String>. There is just List. The compiler puts the automatic cast for you, so you are able in your code to write String s = list.get(i) without casting.

When you declare GenericClass a you are declaring a raw type (you should get a warning on this) thus the compiler does not have a way to know what type the a.getList() is supposed to return. So it uses Object. When you declare GenericClass<Number> a = null; now the compiler knows what type to expect for the a.getList() and uses the desired one.

Edit: It should be clarified that the compiler can know what to expect only if you respect the contract of the signature (i.e. as is the case with GenericClass<Number>). If you don't respect the contract (i.e. you are using a raw type that does not extends Number) then the contract does not apply anymore. The compiler behaves as if no type information was present. Do not forget that the compiler needs to also maintain backwards compatibility with code that was created in the pre-generics era

like image 139
c.s. Avatar answered Oct 12 '22 23:10

c.s.