xcs4me
xcs4me

Reputation: 61

Having trouble wrapping my head around Generics and Inheritance

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

Answers (1)

rzwitserloot
rzwitserloot

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

Related Questions