java - What is a raw type and why shouldn't we use it? -


questions:

  • what raw types in java, , why hear shouldn't used in new code?
  • what alternative if can't use raw types, , how better?

what raw type?

the java language specification defines raw type follows:

jls 4.8 raw types

a raw type defined 1 of:

  • the reference type formed taking name of generic type declaration without accompanying type argument list.

  • an array type element type raw type.

  • a non-static member type of raw type r not inherited superclass or superinterface of r.

here's example illustrate:

public class mytype<e> {     class inner { }     static class nested { }      public static void main(string[] args) {         mytype mt;          // warning: mytype raw type         mytype.inner inn;   // warning: mytype.inner raw type          mytype.nested nest; // no warning: not parameterized type         mytype<object> mt1; // no warning: type parameter given         mytype<?> mt2;      // no warning: type parameter given (wildcard ok!)     } } 

here, mytype<e> parameterized type (jls 4.5). common colloquially refer type mytype short, technically name mytype<e>.

mt has raw type (and generates compilation warning) first bullet point in above definition; inn has raw type second bullet point.

mytype.nested not parameterized type, though it's member type of parameterized type mytype<e>, because it's static.

mt1, , mt2 both declared actual type parameters, they're not raw types.


what's special raw types?

essentially, raw types behaves before generics introduced. is, following entirely legal @ compile-time.

list names = new arraylist(); // warning: raw type! names.add("john"); names.add("mary"); names.add(boolean.false); // not compilation error! 

the above code runs fine, suppose have following:

for (object o : names) {     string name = (string) o;     system.out.println(name); } // throws classcastexception!   //    java.lang.boolean cannot cast java.lang.string 

now run trouble @ run-time, because names contains isn't instanceof string.

presumably, if want names contain string, could perhaps still use raw type , manually check every add yourself, , manually cast string every item names. even better, though not use raw type , let compiler work you, harnessing power of java generics.

list<string> names = new arraylist<string>(); names.add("john"); names.add("mary"); names.add(boolean.false); // compilation error! 

of course, if do want names allow boolean, can declare list<object> names, , above code compile.

see also


how's raw type different using <object> type parameters?

the following quote effective java 2nd edition, item 23: don't use raw types in new code:

just difference between raw type list , parameterized type list<object>? loosely speaking, former has opted out generic type checking, while latter explicitly told compiler capable of holding objects of type. while can pass list<string> parameter of type list, can't pass parameter of type list<object>. there subtyping rules generics, , list<string> subtype of raw type list, not of parameterized type list<object>. consequence, you lose type safety if use raw type list, not if use parameterized type list<object>.

to illustrate point, consider following method takes list<object> , appends new object().

void appendnewobject(list<object> list) {    list.add(new object()); } 

generics in java invariant. list<string> not list<object>, following generate compiler warning:

list<string> names = new arraylist<string>(); appendnewobject(names); // compilation error! 

if had declared appendnewobject take raw type list parameter, compile, , you'd therefore lose type safety generics.

see also


how's raw type different using <?> type parameter?

list<object>, list<string>, etc list<?>, may tempting they're list instead. however, there major difference: since list<e> defines add(e), can't add arbitrary object list<?>. on other hand, since raw type list not have type safety, can add list.

consider following variation of previous snippet:

static void appendnewobject(list<?> list) {     list.add(new object()); // compilation error! } //...  list<string> names = new arraylist<string>(); appendnewobject(names); // part fine! 

the compiler did wonderful job of protecting potentially violating type invariance of list<?>! if had declared parameter raw type list list, code compile, , you'd violate type invariant of list<string> names.


a raw type erasure of type

back jls 4.8:

it possible use type the erasure of parameterized type or erasure of array type element type parameterized type. such type called raw type.

[...]

the superclasses (respectively, superinterfaces) of raw type erasures of superclasses (superinterfaces) of of parameterizations of generic type.

the type of constructor, instance method, or non-static field of raw type c not inherited superclasses or superinterfaces raw type corresponds erasure of type in generic declaration corresponding c.

in simpler terms, when raw type used, constructors, instance methods , non-static fields also erased.

take following example:

class mytype<e> {     list<string> getnames() {         return arrays.aslist("john", "mary");     }      public static void main(string[] args) {         mytype rawtype = new mytype();         // unchecked warning!         // required: list<string> found: list         list<string> names = rawtype.getnames();         // compilation error!         // incompatible types: object cannot converted string         (string str : rawtype.getnames())             system.out.print(str);     } } 

when use raw mytype, getnames becomes erased well, returns raw list!

jls 4.6 continues explain following:

type erasure maps signature of constructor or method signature has no parameterized types or type variables. erasure of constructor or method signature s signature consisting of same name s , erasures of formal parameter types given in s.

the return type of method , type parameters of generic method or constructor undergo erasure if method or constructor's signature erased.

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

the following bug report contains thoughts maurizio cimadamore, compiler dev, , alex buckley, 1 of authors of jls, on why sort of behavior ought occur: https://bugs.openjdk.java.net/browse/jdk-6400189. (in short, makes specification simpler.)


if it's unsafe, why allowed use raw type?

here's quote jls 4.8:

the use of raw types allowed concession compatibility of legacy code. the use of raw types in code written after introduction of genericity java programming language discouraged. possible future versions of java programming language disallow use of raw types.

effective java 2nd edition has add:

given shouldn't use raw types, why did language designers allow them? provide compatibility.

the java platform enter second decade when generics introduced, , there enormous amount of java code in existence did not use generics. deemed critical code remains legal , interoperable new code use generics. had legal pass instances of parameterized types methods designed use ordinary types, , vice versa. requirement, known migration compatibility, drove decision support raw types.

in summary, raw types should never used in new code. you should use parameterized types.


are there no exceptions?

unfortunately, because java generics non-reified, there 2 exceptions raw types must used in new code:

  • class literals, e.g. list.class, not list<string>.class
  • instanceof operand, e.g. o instanceof set, not o instanceof set<string>

see also


Comments