2

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?

2
  • Can you show the declaration of facet before calling create(..)? Or did you mean this.getChild()? Commented Oct 15, 2013 at 18:02
  • Oh, right! I did a lot of code simplification to make the example more straightforward and, yes, instead of facet.getChild() it should simply be this.getChild() as you suggest. I'll update the question. Commented Oct 15, 2013 at 18:33

2 Answers 2

3

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());
}
Sign up to request clarification or add additional context in comments.

6 Comments

Great idea. However, I'm hoping for something without so many if-else-ifs as there are so many components. Perhaps using a visitor...
The advantage of visitor is fewer (as in 0) casts, but certainly not fewer lines.
@JudgeMental I would be able to remove all if/else-ifs from the "catch all" method if I use a visitor, which would be fewer lines.
well, you would need a Visitor interface, an accept( Visitor ) method in each subclass, and your if/else-if clauses would become implementations of the Visitor interface methods in a new class. I'm curious to see how this could be achieved more tersely than that.
@JudgeMental You are right. I should think it through and write some code before I speak next time :) I did write the visitor and as you indicated it eliminated the need for casts. Another side benefit: because each Component must implement accept(Visitor) when I add a new component to the API in the future the code won't compile unless I update the visitor, which ensures no UnsupportedOperationException will be thrown. Thanks for your patience. Thanks for nudging me in the right direction.
|
0

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());
}

2 Comments

Thanks I've updated the question. facet.getChild() is incorrect. It should be 'this.child'.
@PatrickGarner Right. So this is why it happens. As for a solution, check out Tim's. I might attempt something, but no time now.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.