0

Sorry if it's a silly question, but I have searched a long time and couldn't understand why the code below throws me a compilation error:

I have a first class named MenuElement which is parent to a few subclasses. It has 2 methods:

class MenuElement {
   public int getType() {};
   public long getId() {};
   ... 
}

In another class, I have declared as attribute:

private LinkedHashMap<Integer, LinkedHashMap <Long, ? extends MenuElement>> mMenuElement;

And in one of the method, I have this, but the compiler cannot accept menuElement as a chidren of MenuElement:

public void onMenuElementActivationChanged(MenuElement menuElement) {
...
    mMenuElement.get(menuElement.getType()).put(menuElement.getId(), menuElement);
...
}

I feel like I'm maybe missing something with java's generics logic. Someone can help please? Thanks! :)

Kaixin
  • 103
  • 2
  • 8
  • Why don't you use just private LinkedHashMap> mMenuElement; – uoyilmaz Aug 24 '15 at 13:15
  • @Kai-Xin Please write the exact compiler error – Eduardo Yáñez Parareda Aug 24 '15 at 13:18
  • @uoyilmaz Actually, in the constructor, mMenuElement is initialized with a LinkedHashMap> – Kaixin Aug 24 '15 at 13:20
  • That's the problem, initialize it as private LinkedHashMap> mMenuElement; – uoyilmaz Aug 24 '15 at 13:21
  • @Eduardo Yáñez Parareda Error:(737, 32) error: constructor MenuElementsView in class MenuElementsView cannot be applied to given types; required: Context,Dish,LinkedHashMap>,int,boolean,DishDetailsMenuElementViewCallbacks found: Activity,Dish,LinkedHashMap>,int,boolean,MainActivity reason: actual argument LinkedHashMap> cannot be converted to LinkedHashMap> by method invocation conversion – Kaixin Aug 24 '15 at 13:24

3 Answers3

0

This is a common mistake in generics.

The parameter list of a generic must match exactly for it to compile. Thus <? extends X> will not match X because X does not extend X. The following works fine.

abstract class MenuElement {

    public abstract int getType();

    public abstract long getId();
}

public void test() {
    LinkedHashMap<Integer, LinkedHashMap <Long, MenuElement>> mMenuElement = new LinkedHashMap<>();
    MenuElement me;
    mMenuElement.get(me.getType()).put(me.getId(), me);

It is very unusual to need to use <? ...> in generics. In most cases the real type is fine. Normal rules apply for generics type matching anyway - i.e. an object of type B extends A or B implements A can still be used when the generic is defined as accepting an A.

For example:

private static class X {

    public X() {
    }
}

private static class Y {

    public Y() {
    }
}

private static class Z extends Y {

    public Z() {
    }
}

public void test() {
    Map<X, Y> map = new HashMap<>();
    // Both of these work fine.
    map.put(new X(), new Y());
    map.put(new X(), new Z());
}
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
  • Actually, I was wondering why it didn't compile while in my logics, a subclass of MenuElement _is_ a MenuElement... But I guess then what I wanted to do is then just impossible. Thanks for your explanation – Kaixin Aug 24 '15 at 13:37
  • @KaiXin - No - my point is that in most cases what you want to do can be done **without** `?`. See [this answer](http://stackoverflow.com/a/23199614/823393) to a simialr question for some useful links. – OldCurmudgeon Aug 24 '15 at 14:58
0

The way you specify your map, it simply cannot be added to at all.

Basically, your put method is declared as

put(Object key, ? extends MenuElement value)

? extends MenuElement in this case means "some unknow type derived from MenuElement". Not "any type derived from MenuElement": some specific, but unknown type. Thus, you cannot pass any instance of a particular class as second argument of put: none of them belong to that "unknown" type.

That's one of the limitations of wildcards in Java: ? extends makes it impossible to pass data in, but OK to get data out, and ? super makes it impossible to get data out, but possible to pass the data in.

0

The error message you have posted indicates you are using ArrayList instead of LinkedHashMap. The definition expects the Outer Map to have Integer->LinkedHashMap. Please check the code where you are constructing an instance of mMenuElement, and see if you can replace that ArrayList with a Map.

reason: actual argument LinkedHashMap<Integer,ArrayList<? extends MenuElement>> cannot be converted to LinkedHashMap<Integer,LinkedHashMap<Long,? extends MenuElement>> by method 
Mecon
  • 977
  • 1
  • 6
  • 17