Confusing behaviour of overloading

I am experiencing a behaviour that I do not understand related to this code snippet. More precisely, I was expecting the call of getUniqueCost method for the case in which the operator is of type Projection (that is, when n.isProjection() is true) to be calling the method having as signature private double getUniqueCost(final Projection p) instead of that having signature private double getUniqueCost(final Node p).

Note that Projection is a subclass of Node.

Here the code for the two aforementioned methods:

private double getUniqueCost(final Node n){
    if(n.isScan())
        return getUniqueCost(estimateCardinality(n));
    if(n.isJoin())
        return getUniqueCost((NJoin) n);
    if(n.isUnion())
        return getUniqueCost((Union) n);
    if(n.isMaterialization())
        return getUniqueCost(n.getChildren().iterator().next());        
    if(n.isProjection()){ 
        return getUniqueCost(child.isJoin() ? 
            n.getChildren().iterator().next() : ((Projection) n));
    }
    throw new IllegalArgumentException("Unknown node type: " + n.getOperator());
}

private double getUniqueCost(final Projection p){
    return getUniqueCost(estimateCardinality(p)) + 
            getUniqueCost(p.getChildren().iterator().next());
}

The only way to actually manage to call the second method was to modify the first method as follows (the omitted code is the same as before):

private double getUniqueCost(final Node n){
    [...]
    if(n.isProjection()){ 
        final Node child = n.getChildren().iterator().next();
        if(child.isJoin()){
            return getUniqueCost(child);
        }

        final Projection proj = (Projection) n;
        return getUniqueCost(proj);
    }
    throw new IllegalArgumentException("Unknown node type: " + n.getOperator());
}

Given that the cast is executed before actually calling the method (that is, call by value semantics, where the parameters are evaluated before evaluating the method itself), I was expecting it to be sufficient to call the most specific method (the one accepting a parameter of type Projection).

It has been a while since I had a look at the type system of Java, my suspect is that the whole expression child.isJoin() ? n.getChildren().iterator().next() : ((Projection) n) is typed as Node, due to the left part those type is indeed Node.

Does anybody can confirm it? If no, do you have a better understanding of what’s going on here?

In addition, is there a way to have a more compact (elegant?) way of writing the second version of the code?

Answer

The type of your ternary conditional expression – child.isJoin() ? n.getChildren().iterator().next() : ((Projection) n) – is a type that both n.getChildren().iterator().next() and ((Projection) n) can be assigned to. Therefore, if one of them is Node and the other Projection, assuming Projection is a sub-class of Node, the type of the expression is Node.

Your second snippet can be shortened a bit :

    if(child.isJoin()){
        return getUniqueCost(child);
    } else {
        return getUniqueCost((Projection) n);
    }

Casting n to Projection is enough the get the overloaded getUniqueCost(final Projection p) method called. You don’t need an intermediate variable.

Leave a Reply

Your email address will not be published. Required fields are marked *