views:

80

answers:

3

Say I have an interface and some classes:

public interface IPanel<ComponentType extends Component> {
   public void addComponents(Set<ComponentType> components);
   public ComponentType create();
}

public class Button extends Component { }

public class LocalizedButton extends Button { }

public class ButtonsPanel implements IPanel<Button> {
    public void addComponents(Set<Button> components) { ... /* uses create() */ ; }
    public Button create() { return new Button(); }
}

public class LocalizedButtonsPanel extends ButtonsPanel {
    public Button create() { return new LocalizedButton(); }
}

Then I have a set of LocalizedButtons and when I call

final LocalizedButtonsPanel localizedButtonsPanel = new LocalizedButtonsPanel();
final Set<LocalizedButton> localizedButtonsSet = new LinkedHashSet<LocalizedButton>();
localizedButtonsPanel.addComponents(localizedButtonsSet);

I get that this method is not applicable for this arguments. If I try to overload this method as addComponents(Set<LocalizedButton> buttons) in LocalizedButtonsPanel, I get type erasure, of course.

May be some pattern is missed or the trick exists to deal with this architecture to implement correct adding the set of LocalizedButtons?


I've got the answer and I want to make my example more concrete - I have some validators in my implementation, so I need the collection type to be also stored as generic, that is simplified code I've got using the answer:

public interface IPanel<ComponentType extends Component, CollectionType extends Collection<? extends Component>> extends Validated<CollectionType> {
   public void addComponents(CollectionType components);
   public ComponentType create();
}

public class Button extends Component { }

public class LocalizedButton extends Button { }

public class ButtonsPanel implements IPanel<Button, Set<? extends Button>> {
    public void addComponents(Set<? extends Button> components) { ... /* uses create() */ ; }
    public Button create() { return new Button(); }
}

public class LocalizedButtonsPanel extends ButtonsPanel {
    public Button create() { return new LocalizedButton(); }
}

And in this case, it works

+5  A: 

You have

public class ButtonsPanel implements IPanel<Button> {
    public void addComponents(Set<Button> components) { ... /* uses create() */ ; }
    public Button create() { return new Button(); }
}

Should be

public class ButtonsPanel implements IPanel<Button> {
    public void addComponents(Set<? extends Button> components) { ... /* uses create() */ ; }
    public Button create() { return new Button(); }
}

The list is created only for the type of button not for Object that extend that type.

Vash
this: `public class ButtonsPanel implements IPanel<? extends Button>`will not work, because wildcards are not supported in `implements` section, and if I specifythis: `public class GenericButtonsPanel<ButtonType extends Button> implements IPanel<ButtonType>`then `LocalizedButtonsPanel extends GenericButtonsPanel<LocalizedButton>` in this case will not be the child of `ButtonsPanel` that `extends GenericButtonsPanel<Button>` (and I need constructors without generics)
shaman.sir
True, i edited my post.
Vash
+2  A: 

Change the addComponents() signature to

public void addComponents(Set<? extends Button> components)

so that the methods accepts sets of subclasses of Button. This way, you can pass a Set<LocalizedButton> as an argument, because LocalizedButton extends Button and therefore matches the parameter Type of Set<? extends Button>.

f1sh
Oh yes, thank you. My structure was a little bit more complicated (I'll describe in the question), but I've corrected my generics to fit your advise and it really fits :).
shaman.sir
A: 

Parameterised types are not covariant in Java. This is good, otherwise you would be able to add a Dog to a List<Cat> that had been upcast to List<Animal>. You can add covariance at the usage site, e.g., List<? extends Animal> can be assigned a List<Cat> and you can't call its add method.

Ricky Clarkson