7

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;
        ...
    }
}
8

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.