0

I've got "incompatible types" compiler error in condition not yet discussed in stackoverflow (e.g. Why won't this generic java code compile?).

My expectation is simple - I'm calling a templated method which does not use any 'generic' classes from containing class, therefore it should extract types of template parameters from method arguments and this should compile in all cases - but I get "incompatible types" compiler error.

I've noticed strange way to fix the problem - adding "< ? extends Data >" to generic datatype in method arguments. How does it change compiler logic?

In my understanding - there should be no difference because (1) DataContainer class' template parameters are not used in method call and (2) restriction "< TData extends Data >" already defines minimal base object class - Data - and therefore it should be automatically assumed when compiling template without arguments.

public void processData(DataContainer<? extends Data> c) {

(explicit type casting can be used, but i believe it is redundant here)

DummyContextClass dcc = (DummyContextClass)c.getContext(DummyContextClass.class);

Code for copy/paste

public class Test {

    public static class Data {
    }

    public static class DataContainer<TData extends Data> {
        public final TData Data;
        private final Map<String, Object> contexts = new HashMap<>();

        public DataContainer(TData data) {
            Data = data;
        }

        public <T> T getContext(Class<T> type) {
            return (T)contexts.get(type.getName());
        }

        public <T> void setContext(Class<T> type, T context) {
            contexts.put(type.getName(), context);
        }
    }

    public static class DummyContextClass {
    }

    public void processData(DataContainer c) {
        c.setContext(DummyContextClass.class, new DummyContextClass());

        // error: incompatible types: Object cannot be converted to DummyContextClass
        DummyContextClass dcc = c.getContext(DummyContextClass.class);
    }
}
Community
  • 1
  • 1
Xtra Coder
  • 3,389
  • 4
  • 40
  • 59

1 Answers1

6

The problem has nothing (much) to do with your generic methods.
Your problem is you are using a raw type as the parameter of your method:

public void processData(DataContainer c) { // Oops! DataContainer is a raw type

You should use a parameter with DataContainer:

public void processData(DataContainer<? extends Data> c) {

When you leave off the type of a generic class, you have what is called a raw type, and (due to compatibility requirements with old java versions), all generic information is stripped off the class. That (amongst other things) changes all return types that are declared as generic types into Object, so for compilation purposes, your method now looks like:

public Object getContext(Class type) {

...hence your error.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • 1
    You could remove `extends Data` as it's redundant. – Njol Jan 22 '14 at 14:07
  • That seems pointless, as someone can make a non-generic class that has generic methods and that would work just fine. `T` has no relation to `TData`, so there's no reason for that generic info to be stripped. This is as silly as variable arguments treating an unqualified `null` as a null array instead of `new DataType[] {null}` just because people used to use explicit arrays before variable arguments were introduced. – JAB Jan 22 '14 at 14:10
  • @user902383 That should have nothing to do with it, parameter `T` is completely unrelated to `TData`. – JAB Jan 22 '14 at 14:12
  • @JAB regardless, that's the deal. It's in the JLS. – Bohemian Jan 22 '14 at 14:15
  • @Bohemian i feel something is missing here. According to my knowledge, specific 'generic/parameter' types are stripped off at runtime (for backward compatibility), but it is available at compile time. Can you point to exact place in JLS which says that "all generic information is stripped off the class" for compile time? I could not find relevant place here http://docs.oracle.com/javase/specs/jls/se7/html/index.html. – Xtra Coder Jan 23 '14 at 11:05
  • [JLS 4.8](http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.8) covers raw types. I also can't find any reference to removal of all generic info, but that is implied by "making it compatible with pre-generic versions". – Bohemian Jan 23 '14 at 12:58