Java Template Method Pattern with Varying Input Types

design-patternsjavatemplate-method

I am trying to use template pattern to define a generic algorithm in java.
But the method that needs to be overridden, takes an object as input.
This object will vary depending on the concrete implementation.

So I decided to use inheritance in input parameter.
I am passing parent object in the overridden method of the abstract class.
In the overriding method of concrete class I downcast it to the child and get the required details.
It is said that downcasting is a code smell.
How can I achieve this without downcasting.
Or is there any other better solution.

Example code that shows the issue:

abstract class GenericAlgorithm {
    void someMethod(GeneralParameter gp);
}

class ConcreteAlgorithm extends GenericAlgorithm {
    @Override
    void someMethod(GeneralParameter gp) {
        // I can't take a SpecificParameter as argument,
        // so I would need to downcast the parameter
        SpecificParameter sp = (SpecificParameter) gp;
        ...
    }
}

Best Answer

Use generics to specify the parameter type. The parent class would restrict this parameter to be a subclass of GeneralParameter:

abstract class GenericAlgorithm<Param extends GeneralParameter> {
    void someMethod(Param p);
}

Then, the subclass would extend the parent class but provide the used parameter type as a type argument:

class ConcreteAlgorithm extends GenericAlgorithm<SpecificParameter> {
    @Override
    void someMethod(SpecificParameter p) {
        ...
    }
}

Note that this may require you to also use generics for other parts of your program if you want to avoid casting. Here's a full (albeit contrived) example that creates Sandwiches.

abstract class SandwichMaker<S extends Spread> {
    public void make(S spread) {
        toastBread();
        addSpread(spread);
        enjoy();
    }

    protected void toastBread() {
        System.out.println("...toasting bread");
    }

    protected abstract void addSpread(S spread);

    protected void enjoy() {
        System.out.println("this is yummy!");
    }
}

class CheeseSandwichMaker extends SandwichMaker<Cheese> {
    @Override
    protected void addSpread(Cheese cheese) {
        System.out.println("... adding " + cheese.name + " cheese from " + cheese.origin);
    }
}

interface Spread {}
class Cheese implements Spread {
    public final String name;
    public final String origin;

    public Cheese(String name, String origin) {
        this.name = name;
        this.origin = origin;
    }
}

...

SandwichMaker<Cheese> sandwich = new CheeseSandwichMaker();
sandwich.make(new Cheese("Camembert", "Normandy, France"));
sandwich.make(new Cheese("Cheddar", "Somerset, England"));

See it live on ideone.