Patrick Garner
Patrick Garner

Reputation: 3321

Method overloading not working as expected with Java generics

I have a generic class:

public class Facet<C extends Component>
{
    private final C child;

    public Facet(C child) { this.child = child; }

    public C getChild() { return this.child; }

    public UIComponent getViewComponent() {
        return PrimeFacesComponentFactory.create(facesContext, this.getChild());
    }
}

I have a component factory that also has a bunch of methods, several of which look like this:

public static UIComponent create(FacesContext fc, Component component) {
    throw new UnsupportedOperationException("Method not yet implemented for class: " + component.getClass().getName());
}

public static UIRow create(FacesContext fc, Row modelRow) { ... }

public static UIColumn create(FacesContext fc, Column modelColumn) { ... }

NOTE: in each of the factory methods the 2nd argument is an object that extends Component and what is returned is always a subclass of UIComponent. The first factory method (the one that throws the exception) is intended to be a "catch all," which is invoked if there is no specific factory method written for a particular component.

What I'm trying to do: the correct factory method should be invoked depending on what type of component facet's child is. What's happening is that the first factory method is always invoked (e.g. the one that throws the exception). This is counterintuitive to me, since the type of C is known at runtime and C is "some specific type that extends Component."

Debugging, I set a breakpoint at Facet#getViewComponent, invoking it when I knew that facet.getChild() would return an object of type Row. So in this example C was Row. It surprised me to see that when the factory method was invoked e.g.

PrimeFacesComponentFactory.create(facesContext, facet.getChild());

program execution flowed to the "catch all" factory method! If, however, I explicitly cast facet.getChild() as follows:

PrimeFacesComponentFactory.create(facesContext, (Row)facet.getChild());

then the specific method was invoked. Weird, because I simply/redundantly cast a Row to a Row.

I've read some about why this is happening, but it's still hazy and counterintuitive to my mind. At any rate, how can I resolve this problem?

Upvotes: 2

Views: 268

Answers (2)

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279880

With the following declaration

public C getChild() { return this.child; }

public UIComponent getViewComponent() {
    return PrimeFacesComponentFactory.create(facesContext, facet.getChild());
}

assuming facet is meant to be this, at compile time, the only thing that is known about the type parameter C is that it extends Component. So the compiler binds this method call with the method declared to accept a Component.

public static UIComponent create(FacesContext fc, Component component) {
    throw new UnsupportedOperationException("Method not yet implemented for class: " + component.getClass().getName());
}

Upvotes: 0

Tim S.
Tim S.

Reputation: 56536

The overload resolution happens once, at compile-time, for the Facet class, not for each C in Facet<C>, and not at run time. A potential solution is to make your catch-all method smarter:

public static UIComponent create(FacesContext fc, Component component) {
    if (component instanceof Row)
        return create(fc, (Row)component);
    else if (component instanceof Column)
        return create(fc, (Column)component);
    // include other types, and lastly
    else
        throw new UnsupportedOperationException("Method not yet implemented for class: " + component.getClass().getName());
}

Upvotes: 3

Related Questions