Question

I am working with code where the authors used raw types inside of generic definitions. It seems like the compiler does not recognize generic types at all when a raw type is used, even if the generic type is defined explicitly in the raw class.

Let me explain this with an example. We have a bean:

class Bean<T> {
   private List<String> list = new ArrayList<String>();
   private T object;
   public T getObject() {
     return object;
   }
   public void setObject(T object) {
     this.object = object;
   }
   public List<String> getList() {
     return list;
   }
}

And a wrapper (the code is using Apache Wicket, I am just abstracting this away):

class Wrapper<S> {
  private S wrapped;
  public S getWrapped() {
    return wrapped;
  }
  public void setWrapped(S wrapped){
    this.wrapped = wrapped;
  }
}

Now our folks used the Wrapper in a semi-generic manner where the compiler does not give a warning (unfortunately):

Wrapper<Bean> wrapper = new Wrapper<Bean>();
wrapper.setWrapped(new Bean<Integer>());
// The next line returns a raw type even though the List<String>
// type is stated explicitly in Bean.class and known by the compiler
wrapper.getWrapped().getList(); 

Of course, we should not use the raw type but declare Wrapper<Bean<?>> instead. Nevertheless, I am wondering why the compiler is so strict on this. Is the compiler setting some sort of raw flag when resolving the type and ignores all generic information, even the one that is known? This is what it looks like to me. I could find an explicit answer to this reading the corresponding section in the JLS. I'd also like to know if there is a reasoning behind this decision.

Was it helpful?

Solution

Using the raw type means using the erasure of that type. This means that all generic type information is erased (an interesting question would be why this is not the case for static fields). As that part of the JLS that you are referring to indicates, raw types are to be used in legacy (pre-1.5) code only (bar some specific cases where you have no other option). That legacy code won't be able to use any generic type information anyway, so there would be no point in keeping the information that could in theory be known.

OTHER TIPS

Well with raw type, we are at risk of type unsafe operation and The warning shows that raw types bypass generic type checks, deferring the catch of unsafe code to runtime. Continuing with your example:

Wrapper<Bean> wrapper = new Wrapper<>();//<--- raw type warning
Bean<Integer>aBean = new Bean<>();
aBean.setObject(1);  //<--- setting object of integer
wrapper.setWrapped(aBean);
Bean<String>beanString = wrapper.getWrapped(); //<--- allowed but with warning
String s = beanString.getObject();//<--- now what ? Should it be allowed ?

With the above example, as the comment says: There should be an error generated for wrong type casting, but it is allowed at compile type because of the raw type instead the compiler generates warning where The term "unchecked" means that the compiler does not have enough type information to perform all type checks necessary to ensure type safety. However, at run-time an error will be generated resulting in unnecessary overhead.

The official tutorial page of Raw Types explains this.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top