Diagnosing Raw Types

Raw types and unchecked warnings

Raw types are often associated with the concept of unchecked warning - that is the warnings emitted by the compiler in order to warn the user about potentially unsafe code. Currently, the compiler emit an unchecked warning in one of the following cases:

1. A non-static field of a raw type gets assigned

Since raw types do not provide any instantiation to a generic class' type parameters, such assignments should be regarded as potentially unsafe - i.e. if type-erasure changes the type of the field.

class Foo<X extends Number> { X x; }
Foo raw_foo = ...
raw_foo.x = 1;
//warning: unchecked assignment to raw type member

2. A non-static method of a raw types is invoked

For the reasons above, a method call on a raw type should also be regarded as potentially unsafe - i.e. if type-erasure changes any of the argument/return types of the method.

List raw_list = ...
list_raw.add("Hello!"); //warning: unchecked call to raw type member

3. An unchecked conversion

Happens e.g. in an assignment where (i) the RHS is raw and (ii) the LHS is fully parameterized (e.g. it's not an unbounded wildcard)

List raw_list = ...
List<String> list_string = ...
list_string = list_raw; //warning: unchecked conversion

4. An unchecked method call

Happens when an unchecked conversion (see above) is needed to convert an actual argument's type into the corresponding formal's. When method resolution goes through an unchecked conversion, the resolved method's signature gets erased.

List raw_list = ...
Object max = Collections.max(raw_list); //warning: unchecked method invocation

5. An unchecked cast

Given a cast from S to T, a cast is unchecked if the target type T and if S is not a subtype of T (well there's a bit more, see JLS rules)

Object o = new ArrayList<String>();
List<String> list_string = (List<String>)o; //warning: unchecked cast

Harmless raw types

There are, however, a number of situations in which raw types do not cause any unchecked warning; in other words using a raw type in a Java program does not make that program unsafe. Since unchecked warnings only concern about safety/heap pollution issues, it follows that there's currently no way for catching all harmless raw types in a given source. When is a raw type harmless? Here's a (partial) list of situations to give you an idea:

1. The type of a variable is raw

Since a raw type C is a supertype of all generic instantiation of C, it follows that a raw type can also be used to abstract over different parameterization of the same generic class. In other words, the raw type is used as if it were an unbounded wildcard.

List raw_list = ... //raw type should be replaced by List<?>
List<String> list_string = ...
List<Integer> list_integer = ...

list = list_string;
list = list_integer;

2. The formal type of a method argument is raw

The aim is (again) to abstract away from the concrete parameterization of a generic type, so that the method can be applied to more arguments; the raw type is acting as an unbounded wildcard.

void doSomething(List list) {
  for (Object o : list)
    System.out.println(o);
}

3. The type of a new expression is raw

What's the point of creating raw objects? I'll just say a word: arrays. However keep in mind creating an array of unbounded wildcard is legal and should be preferred.

List<?>[] arr = new List[42]; //same as new List<?>[42]
arr[0] = new ArrayList<String>();

4. The type of an instanceof/cast expression is raw

This happens very frequently, as javac forbids instanceof expressions whose target type is a generic type; for casts, the compiler is slightly more permissive since casts to generic type are allowed but a warning is issued (see above). Anyway, the raw type should be replaced by an unbounded wildcard, as they have similar properties w.r.t. subtyping.

Object o = new ArrayList<String>();
List<?> list_string = (List)o; //same as (List<?>)o
boolean b = o instanceof List; //same as o instanceof List<?>

5. A type parameter is raw

Raw types can be exploited in a generic class' parameterization. A raw parameter can often be replaced by an unbounded wildcard, as the resulting generic type can still be used in contexts such as new expressions, extends/implements clauses, etc.

List<List> l = ... //same as List<List<?>>
l = new ArrayList<List>(); //same as new ArrayList<List<?>>

Catching all raw types

If you want to make sure that your Java program is 100% raw type-free javac is currently of little help. This is why we decided to introduce a new Xlint flag that control the generation of so-called raw type warnings. When the -Xlint:rawtypes flag is enabled any use of a raw type name will give a warning, unless suppressed in the usual way with @SuppressWarnings("rawtypes"). Consider the following example:

class Foo<X> {
Foo f = null;
    void m(Foo<Foo> a) {
        Foo<?> foo = new Foo();
        boolean b = foo instanceof Foo;
    }
}

If you compile the program above using the new -Xlint:rawtypes flag (this flag will be available in the next OpenJDK build) you'll get the following output:

Foo.java:2: warning: [rawtypes] found raw type: Foo
missing type parameters for generic class Foo<X>
    Foo f = null;
    \^
Foo.java:3: warning: [rawtypes] found raw type: Foo
missing type parameters for generic class Foo<X>
    void m(Foo<Foo> a) {
               \^
Foo.java:4: warning: [rawtypes] found raw type: Foo
missing type parameters for generic class Foo<X>
        Foo<?> foo = new Foo();
                         \^
Foo.java:5: warning: [rawtypes] found raw type: Foo
missing type parameters for generic class Foo<X>
        boolean b = foo instanceof Foo;
                                   \^
4 warnings


Cool huh? Thanks to this flag we were able to detect 27 raw type warnings in the langtools workspace only - raw types won't be hiding from us any longer!

Thanks to Jon (and others) - for suggesting me the idea - and for all the feedback and support

Comments:

Eclipse already implements this kind of check
but doesn't raise a warning in case of instanceof on
a raw type (foo instanceof Foo).

Is there a rational to emit a warning in that case ?

Another question, is there a chance to revisit getClass() typing rule
(see bug 6184881) which introduce a raw type instead of a wildcard ?

cheers,
Rémi

Posted by Rémi Forax on October 07, 2008 at 02:35 AM BST #

Yes, Eclipse already does this; in our case, the new -Xlint option is mainly meant for developers who want to get rid of all raw types in their code. From that point of view, an instanceof whose target type is raw should raise a warning, as it could be easily replaced by an unbounded wildcard. You can think of this new warning flag as a way to enforce a 'true' JDK 5.0 programming style.

Regarding your other question, I think that the idea of replacing the raw type returned by getClass() with an unbounded wildcard is good and relatively easy to implement. On the other hand I have the feeling that the choice of returning a raw type is deliberate and has to do with preserving migration compatibility. I should investigate this a little bit!

Posted by Maurizio Cimadamore on October 07, 2008 at 04:19 AM BST #

I managed to find the rationale behind the raw type in the return type of
Object.getClass. Here's a couple of links:

[1] http://bugs.sun.com/view_bug.do?bug_id=5004321
[2] http://bugs.sun.com/view_bug.do?bug_id=4982096
[3] http://forums.sun.com/thread.jspa?threadID=496028&forumID=316

First of all, Object.getClass returns a wildcard because code like the
following must compile (it used to compile with Java < 1.5), see [1,3]:

if (new String()).getClass() == (new Integer(5)).getClass()) { ... }

Secondly, returning Class<? extends |X|> is more conservative than
returning Class<? extends wildcard(X)>, where wildcard(X) is an unbounded
wildcard if X is a parameterized type. Consider the following sample:

class ClassLit<T> {
public test m() {
Class<? extends ClassLit> cl1 = null;
Class<? extends ClassLit<?>> cl2 = null;
ClassLit<Integer> ci1 = cl1.cast(null); //warning
ClassLit<Integer> ci2 = cl2.cast(null); //error
}
}

I think that a warning is more appropriate here; an error would end up in
rejecting a lot of 'good' code. Perhaps this deserves a blog entry on its own!

Posted by Maurizio Cimadamore on October 07, 2008 at 09:22 AM BST #

Sorry for my late answer,
I don't see why this code is 'good'.
You have a class of a subtype of ClassLit,
you cast null as this class and assign the result
into a ClassLit of Integer.
Perhaps it can work at runtime but not in all cases
so it's a compile error, if you want that you have
to use an unsafe cast.

ClassLit<Integer> ci2 = (ClassLit<Integer>)cl2.cast(null); //warning

Anyway, currently the implementation is not logical:
instanceof ClassList emit a warning
but getClass() returns a Class<? extends RawType>.
The former says the runtime class must be checked using a wildcard
and the later says a runtime class is a raw type.

Rémi

Posted by Rémi Forax on November 03, 2008 at 02:35 AM GMT #

Hi Remi
thanks for the reply; I'm not saying that the code above is good - I said 'good' (note the quotes :D). From a theoretical point of view I agree with you, the code I've written should be rejected or an unchecked cast should be exploited. On the other hand, from a pragmatic point of view your change is risky - as it could potentially break existing code.

As far as raw types warnings are currently implemented, here's a list of proposals that could improve their usability (I collected those proposals from the comments I've received on the compiler-dev mailing list):

\*) Have a special rule for Class<>, i.e allow Class of a raw type without emitting any warning

\*) Suppress raw warnings for instanceof (the addition of <?> adds noise without providing any safety) - should we also suppress them for cast as well?

Any other comments/situation in which you'd like having the warning automatically suppressed by javac?

Posted by Maurizio Cimadamore on November 03, 2008 at 04:04 AM GMT #

A wise man once said something like,

"objects have class; variables have type"

jls 3rd edition says,

"The type of a class literal, C.Class, where C is the name of a class, interface or array type, is Class<C>."

BUG REPORT: C.Class should be C.class

But note how this produces a rare type when C is generic.

Posted by Martin Buchholz on November 03, 2008 at 08:35 AM GMT #

I forgot to make this point:

instanceof is a runtime operator whose second argument is a \*class\*,
not a type.

In a pre-reification world,

x instanceof List

makes a lot more sense than

"x instanceof List<QUESTION_MARK>"

Getting users to write

"x instanceof List<QUESTION_MARK>"

when they really wanted to write

"x instanceof List<E>"

is not an improvement.

Posted by Martin Buchholz on November 03, 2008 at 09:41 AM GMT #

@Martin
JLS 3rd edition - instanceof is defined as 'type comparison operator'. See http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.20.2

The second argument of an instanceof should be a reifiable (see JLS 4.7) reference type (and not a class, as you say)- and using an unbounded wildcard should be preferred to a raw type (JLS 4.8 - "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 genericity 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").

The JLS already define a reifiable type as a type which is available at runtime; the fact that the JVM reifies both List<?> and List in the same way doesn't mean that using a raw type is as good as using an unbounded wildcard.

Regarding the issue about C.class generating a rare type, there's already a bug report for that - see http://bugs.sun.com/view_bug.do?bug_id=6184881 and \*all\* related issues (esp. those regarding backward compatibility) - and, frankly, I don't think this is the right place for submitting bugs against the JLS.

Posted by Maurizio Cimadamore on November 05, 2008 at 01:46 AM GMT #

Forget warnings, you should begin issuing compiler \*errors\* on raw types!

Developers have had almost a century to migrate already. Enough is enough. Java7 should introduce Reified Generics and issue compiler errors on raw-types.

Posted by Gili on November 05, 2008 at 09:19 PM GMT #

I am not sure if it is not an improvement already. Indeed it provides a clue that the user noticed the type was generic; but I agree you'd like to use List<E> at the end of the day (once you have generics at runtime).

Posted by Philippe Mulet on November 06, 2008 at 09:56 AM GMT #

I totally agree with what Martin Buchholz says about instanceof.

And I totally disagree with what Gili says about raising errors instead of warnings:
If you really deal deep with generics you will come into situations where there is no other way than bypassing the compilers generic type checks to implement your functionality.

Posted by Jörg Hohwiller on July 03, 2009 at 02:30 PM BST #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Maurizio Cimadamore is a member of the langtools team based in Santa Clara, CA. His efforts are mainly focused on the type-system area of the Java compiler.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today