Reputation: 61
Im in a bit of entanglement when it comes to generics and inheritance. The overall idea is a builder pattern for a variety of components and group or container components containing other components. Some component groups require a specific component some can be any components. That's where the problem lies. Best explained in code I think:
public abstract class AbstractGroupComponentBuilder <T extends ComponentGroup<R>, R extends AbstractDashboardComponent> implements ComponentBuilder<ComponentGroup<R>> {
private List<ComponentBuilder<R>> children;
protected void child(ComponentBuilder<R> builder){
children.add(builder);
}
...
}
public class ComponentGroupBuilder extends AbstractGroupComponentBuilder<ComponentGroup<AbstractDashboardComponent>, AbstractDashboardComponent>{
public <T> TableBuilder<T> table(Class<T> clazz){
TableBuilder<T> builder = new TableBuilder<T>(clazz);
child(builder); // PROBLEM HERE: The method child(ComponentBuilder<AbstractDashboardComponent>) in the type AbstractGroupComponentBuilder<ComponentGroup<AbstractDashboardComponent>,AbstractDashboardComponent> is not applicable for the arguments (TableBuilder<T>)
}
...
}
public class TableBuilder<T> implements ComponentBuilder<Table> {
...
}
public class Table extends AbstractDashboardComponent{
...
}
public class ComponentGroup<T extends AbstractDashboardComponent> extends AbstractDashboardComponent {
...
}
public interface ComponentBuilder<T extends AbstractDashboardComponent> {
public T build();
}
So the error the compiler has is: "The method child(ComponentBuilder<AbstractDashboardComponent>) in the type AbstractGroupComponentBuilder<ComponentGroup<AbstractDashboardComponent>,AbstractDashboardComponent> is not applicable for the arguments (TableBuilder<T>)"
How come that is not compatible since TableBuilder<T> extends ComponentBuilder<Table>
and 'Table extends AbstractDashboardComponent'.
Upvotes: 0
Views: 60
Reputation: 102795
Here, your builder is of type TableBuilder<Table>
. This is not compatible with ComponentBuilder<AbstractDashboardComponent>
. The prolbem isnt ComponentBuilder
, it's the stuff inside the <>
. Unlike normal types, the stuff in side <>
is invariant, meaning, the types have to match exactly, no subtype allowed.
Here is why: I'm going to use very simple and familiar types to explain it:
List<Integer> ints = new ArrayList<Integer>();
List<Number> numbers = ints; // PROBLEM LINE
numbers.add(5.0);
ints.get(0); // uhoh - that's a double and not an int!
The line marked PROBLEM
? That won't compile. The fix is this:
List<Integer> ints = new ArrayList<Integer>();
List<? extends Number> numbers = ints; // this is fine
numbers.add(5.0); // PROBLEM LINE
ints.get(0); // uhoh - that's a double and not an int!
In this example, the error is on the third line: You cannot add ANYTHING to a List<? extends NothingYouTypeHereCanFixThat>
(irrelevant detail: except the literal null
).
Which is why line 2 is now fine.
You need to do the same thing and throw some ? extends
around until it compiles.
<T>
= invariant; only precise Ts work out, but you can use T in all ways you'd want.
<? extends T>
= covariant; T or any subtype of T is fine, but you can only call get-style methods.
<? super T>
= contravariant; T or any SUPERTYPE of T is fine, but you can only call add-style methods (you can call get, but you'd just get Object).
contravariant rarely comes up. It lets you do:
List<? super Integer> list = ...;
list.add(5); // compiles fine.
Object o = list.get(0); // all you get is `Object` here.
and if you have a List<Number>
, you can assign it to the list variable.
Upvotes: 1