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;
...
}
}
1 Answer 1
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.
Explore related questions
See similar questions with these tags.