Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why won't this generic java code compile?

Tags:

java

generics

In this simplified example I have a generic class, and a method that returns a Map regardless of the type parameter. Why does the compiler wipe out the types on the map when I don't specify a type on the containing class?

import java.util.Map;  public class MyClass<T> {     public Map<String, String> getMap()     {            return null;     }      public void test()     {            MyClass<Object> success = new MyClass<Object>();         String s = success.getMap().get("");          MyClass unchecked = new MyClass();         Map<String, String> map = unchecked.getMap();  // Unchecked warning, why?         String s2 = map.get("");          MyClass fail = new MyClass();         String s3 = fail.getMap().get("");  // Compiler error, why?     } } 

I get this compiler error.

MyClass.java:20: incompatible types found   : java.lang.Object required: java.lang.String                 String s3 = fail.getMap().get("");  // Compiler error 
like image 692
Craig P. Motlin Avatar asked Mar 19 '09 13:03

Craig P. Motlin


People also ask

How do you declare a generic type in Java?

To update the Box class to use generics, you create a generic type declaration by changing the code "public class Box" to "public class Box<T>". This introduces the type variable, T, that can be used anywhere inside the class. As you can see, all occurrences of Object are replaced by T.

What does generic mean in Java?

Generics means parameterized types. The idea is to allow type (Integer, String, … etc., and user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is possible to create classes that work with different data types.

Why is generic needed 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.

Where is generic used in Java?

Generics should be used instead of raw types ( Collection< T > instead of Collection , Callable< T > instead of Callable , …) or Object to guarantee type safety, define clear type constraints on the contracts and algorithms, and significantly ease the code maintenance and refactoring.


2 Answers

Got it. This actually isn't a bug, strange as it might seem.

From section 4.8 (raw types) of the JLS:

The type of a constructor (§8.8), instance method (§8.8, §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 erasure of its type in the generic declaration corresponding to C. The type of a static member of a raw type C is the same as its type in the generic declaration corresponding to C.

So even though the method's type signature doesn't use any type parameters of the class itself, type erasure kicks in and the signature becomes effectively

public Map getMap() 

In other words, I think you can imagine a raw type as being the same API as the generic type but with all <X> bits removed from everywhere (in the API, not the implementation).

EDIT: This code:

MyClass unchecked = new MyClass(); Map<String, String> map = unchecked.getMap();  // Unchecked warning, why? String s2 = map.get(""); 

compiles because there's an implicit but unchecked conversion from the raw Map type to Map<String, String>. You can get the same effect by making an explicit conversion (which does nothing at execution time) in the last case:

// Compiles, but with an unchecked warning String x = ((Map<String, String>)fail.getMap()).get(""); 
like image 125
Jon Skeet Avatar answered Sep 21 '22 14:09

Jon Skeet


Hm ... unfortunately I can't tell you why it fails. But I can give you a simple workaround:

Change the type of fail to MyClass<?>, then it will compile just fine.

like image 27
Joachim Sauer Avatar answered Sep 22 '22 14:09

Joachim Sauer