I am implementing Hierarchical Builder Pattern. I have used generics to make return type of my Parent Builder setter as that for child. Still I am facing problem when I use the child setter method after calling parent setter method. I don't want to define my child specific method (setEngine(String) here) in my Parent Builder. Is there any other way around for this problem?
I have made an example snippet for the mentioned problem, identical to this case.
CarFactory -> It returns the object of specific car that user wants
Car -> Parent for all Car types, Swift, Duster etc
Swift -> Specific car
Parent->Child Hierarchy
Car -> Swift
CarBuilder -> SwiftBuilder
Car.java
package Builders;
public class Car {
int tyre;
int seat;
public int getTyre() {
return tyre;
}
public void setTyre(int tyre) {
this.tyre = tyre;
}
public int getSeat() {
return seat;
}
public void setSeat(int seat) {
this.seat = seat;
}
}
Swift.java
package Builders;
public class Swift extends Car {
boolean safetyAirbag;
String engine;
public boolean isSafetyAirbag() {
return safetyAirbag;
}
public String getEngine() {
return engine;
}
public void setSafetyAirbag(boolean safetyAirbag) {
this.safetyAirbag = safetyAirbag;
}
public void setEngine(String engine) {
this.engine = engine;
}
}
CarBuilder.java
package Builders;
public abstract class CarBuilder {
int tyre;
int seat;
public abstract <B extends CarBuilder>B self();
public abstract <T extends Car>T typeOfCar();
public <B extends CarBuilder>B setTyre(int tyre) {
this.tyre = tyre;
return self();
}
public <B extends CarBuilder> B setSeat(int seat) {
this.seat = seat;
return self();
}
public <C extends Car>C build()
{ C car=this.typeOfCar();
car.setSeat(seat);
car.setTyre(tyre);
return car;
}
}
SwiftBuilder.java
package Builders;
public class SwiftBuilder extends CarBuilder {
String engine;
#Override
public
SwiftBuilder self() {
return this;
}
#Override
public
Swift typeOfCar() {
return new Swift();
}
public SwiftBuilder setEngine(String string) {
this.engine=string;
return this;
}
public Swift build()
{ Swift s=(Swift)super.build();
return s;
}
}
CarFactory.java
package Builders;
public class CarFactory {
public SwiftBuilder getSwiftDesire()
{
return new SwiftBuilder();
}
}
Drivers.java
package Builders;
public class Drivers {
Swift getMyCar() {
Swift s= this.factory().getSwiftDesire().setSeat(4).setEngine("CC").build();
return s;
}
CarFactory factory() {
return new CarFactory();
}
}
In Drivers.java class I am not able to use setEngine() method after setSeat() method,
this.factory().getSwiftDesire().setSeat(4).setEngine("CC").build();
I don't want to declare setEngine in parent class, is there any way around for same?
Thank you in advance!
You need to use generics on the class level, not on the method level, in your CarBuilder:
package Builders;
public abstract class CarBuilder<B extends CarBuilder<B, C>, C extends Car> {
int tyre;
int seat;
public abstract B self();
public abstract C typeOfCar();
public B setTyre(int tyre) {
this.tyre = tyre;
return self();
}
public B setSeat(int seat) {
this.seat = seat;
return self();
}
public C build() {
C car = this.typeOfCar();
car.setSeat(seat);
car.setTyre(tyre);
return car;
}
}
And then you define your SwiftBuilder:
package Builders;
public class SwiftBuilder extends CarBuilder<SwiftBuilder, Swift> {
String engine;
#Override
public SwiftBuilder self() {
return this;
}
#Override
public Swift typeOfCar() {
return new Swift();
}
public SwiftBuilder setEngine(String string) {
this.engine = string;
return this;
}
public Swift build() {
Swift s = super.build();
s.setEngine(engine);
return s;
}
}
And it works.
Related
I am creating DTO structure with Builder pattern. Because of existence of many requests I created parent request AbstractRequest to create concrete requests - e.g. ConcreteRequest in this example.
Base Buildable interface defines contract to all Requests.
public interface Buildable<T> {
T build();
void validate();
}
Parent request AbstractRequest to create concrete ConcreteRequest that holds parameters used by all descendants (for brevity globalValue only in this example).
public abstract class AbstractRequest {
private final String globalValue;
public AbstractRequest(BuilderImpl builder) {
this.globalValue = builder.global;
}
public interface Builder<T> extends Buildable<T> {
Builder<T> globalValue(String globalValue);
}
public abstract static class BuilderImpl<T> implements Builder<T> {
private String global;
#Override
public Builder<T> globalValue(String globalValue) {
this.global = globalValue;
return this;
}
}
}
Concrete request that has one private parameter localValue:
public final class ConcreteRequest extends AbstractRequest {
private final String localValue;
public ConcreteRequest(BuilderImpl builder) {
super(builder);
this.localValue = builder.localValue;
}
public String getLocalValue() {
return localValue;
}
public static Builder builder(){
return new BuilderImpl();
}
public interface Builder extends AbstractRequest.Builder<ConcreteRequest> {
Builder localValue(String localValue);
}
public static final class BuilderImpl extends AbstractRequest.BuilderImpl<ConcreteRequest> implements Builder {
private String localValue;
#Override
public ConcreteRequest build() {
this.validate();
return new ConcreteRequest(this);
}
#Override
public void validate() {
// do validation
}
#Override
public Builder localValue(String localValue) {
this.localValue = localValue;
return this;
}
}
}
Q: Why is not ConcreteRequest#getLocalValue accessible while ConcreteRequest#build is available?
I modified my code and it seems it works.
public interface Buildable<T> {
T build();
void validate();
}
Parent class:
public abstract class AbstractRequest {
private final String globalValue;
public AbstractRequest(BuilderImpl builder) {
this.globalValue = builder.global;
}
public String getGlobalValue() {
return globalValue;
}
public interface Builder<B extends Builder, C extends AbstractRequest> extends Buildable<C> {
B globalValue(String globalValue);
}
public abstract static class BuilderImpl<B extends Builder, C extends AbstractRequest> implements Builder<B, C> {
private String global;
#Override
public B globalValue(String globalValue) {
this.global = globalValue;
return (B) this;
}
#Override
public void validate() {
if (global == null) {
throw new IllegalArgumentException("Global must not be null");
}
}
}
}
and
public final class ConcreteRequest extends AbstractRequest {
private final String localValue;
public ConcreteRequest(BuilderImpl builder) {
super(builder);
this.localValue = builder.localValue;
}
public static Builder builder() {
return new BuilderImpl();
}
public String getLocalValue() {
return localValue;
}
public interface Builder extends AbstractRequest.Builder<Builder, ConcreteRequest> {
Builder localValue(String localValue);
}
public static final class BuilderImpl extends AbstractRequest.BuilderImpl<Builder, ConcreteRequest> implements Builder {
private String localValue;
#Override
public Builder localValue(String localValue) {
this.localValue = localValue;
return this;
}
#Override
public ConcreteRequest build() {
this.validate();
return new ConcreteRequest(this);
}
#Override
public void validate() {
super.validate();
if (localValue == null) {
throw new IllegalArgumentException("Local must not be null");
}
}
}
}
And now I can see all methods:
I have this class structure:
Stuff
/ \
Food
|
FastFood Drink
| |
Pizza Juice
/ \ / \
| SicilianPizza | OrangeJuice
| AppleJuice
CaliforniaPizza
I can create pizza and juice objects using builders like this:
CaliforniaPizza californiaPizze = CaliforniaPizza.builder(2)
.addTopping("Tomatoes").addOlives(true).build();
SicilianPizza sicilianPizza = SicilianPizza.builder(1)
.addTopping("Bacon").addCheese(false).build();
AppleJuice appleJuice = AppleJuice.builder(40)
.setPrice(120).setAlcoholVolume(0).setAppleColor("yellow").build();
OrangeJuice orangeJuice = OrangeJuice.builder(35).setOrangeSize(8).build();
Every setter method of the builder returns correct object:
I'd like to create a method that would accept a FastFood.Builder object and use it to create a FastFood object that would pass it to another method:
public void makeFat(FastFood fastFood) {...}
But I can't: setter methods don't return FastFood, but rather its parents:
Is there something wrong with my generics? How could that be accomplished?
package pizza;
import java.util.*;
class TestStuff {
public static void main(String[] args) {
CaliforniaPizza californiaPizze = CaliforniaPizza.builder(2)
.addTopping("Tomatoes").addOlives(true).build();
SicilianPizza sicilianPizza = SicilianPizza.builder(1)
.addTopping("Bacon").addCheese(false).build();
AppleJuice appleJuice = AppleJuice.builder(40)
.setPrice(120).setAlcoholVolume(0).setAppleColor("yellow").build();
OrangeJuice orangeJuice = OrangeJuice.builder(35).setOrangeSize(8).build();
}
public FastFood testFood(FastFood.Builder fastFoodBuilder) {
//return fastFoodBuilder.setMealType("fd").addOlives(true).setPrice(20).build();
}
public void makeFat(FastFood fastFood) {}
}
abstract class Stuff {
protected double price;
protected Stuff() {}
protected abstract class Builder<T extends Builder<T>> {
protected abstract Stuff build();
protected abstract T self();
public T setPrice(double price) {
Stuff.this.price = price;
return self();
}
}
}
abstract class Food extends Stuff {
protected String mealType; //breakfast/dinner/etc
protected Food() {}
public abstract class Builder<T extends Builder<T>> extends Stuff.Builder<T> {
protected abstract Food build();
protected abstract T self();
public T setMealType(String mealType) {
Food.this.mealType = mealType;
return self();
}
}
}
abstract class FastFood extends Food {
protected int harm;
protected FastFood() {}
public abstract class Builder<T extends Builder<T>> extends Food.Builder<T> {
protected abstract FastFood build();
protected abstract T self();
public T setHarm(int harm) {
FastFood.this.harm = harm;
return self();
}
}
}
abstract class Pizza extends FastFood {
protected List<String> toppings = new ArrayList<>(); //optional
protected int size; //obligatory
protected Pizza(int size) {this.size = size;}
public abstract class Builder<T extends Builder<T>> extends FastFood.Builder<T> {
public T addTopping(String topping) {
toppings.add(topping);
return self();
}
}
}
class CaliforniaPizza extends Pizza {
private boolean addOlives;
private CaliforniaPizza(int size) {super(size);}
public static Builder builder(int size) {return new CaliforniaPizza(size).new Builder();}
public class Builder extends Pizza.Builder<Builder> {
#Override
public CaliforniaPizza build() {
return CaliforniaPizza.this;
}
#Override
public Builder self() {return this;}
public Builder addOlives(boolean addOlives) {
CaliforniaPizza.this.addOlives = addOlives;
return this;
}
}
}
class SicilianPizza extends Pizza {
private boolean addCheese;
private SicilianPizza(int size) {super(size);}
public static Builder builder(int size) {
return new SicilianPizza(size).new Builder();
}
public class Builder extends Pizza.Builder<Builder> {
#Override
public SicilianPizza build() {return SicilianPizza.this;}
#Override
public Builder self() {return this;}
public Builder addCheese(boolean addCheese) {
SicilianPizza.this.addCheese = addCheese;
return this;
}
}
}
abstract class Drink extends Stuff {
protected double density;
protected double alcoholVolume;
protected Drink(double density) {this.density = density;}
public abstract class Builder<T extends Builder<T>> extends Stuff.Builder<T> {
protected abstract Drink build();
protected abstract T self();
public T setAlcoholVolume(double alcoholVolume) {
Drink.this.alcoholVolume = alcoholVolume;
return self();
}
}
}
abstract class Juice extends Drink {
private String color;
protected Juice(double density) {super(density);}
public abstract class Builder<T extends Builder<T>> extends Drink.Builder<T> {
public Builder<T> setColor(String color) {
Juice.this.color = color;
return self();
}
}
}
class AppleJuice extends Juice {
private String appleColor;
private AppleJuice(double density) {super(density);}
public static Builder builder(double density) {return new AppleJuice(density).new Builder();}
public class Builder extends Juice.Builder<Builder> {
#Override
public AppleJuice build() {
return AppleJuice.this;
}
#Override
public Builder self() {
return this;
}
public Builder setAppleColor(String appleColor) {
AppleJuice.this.appleColor = appleColor;
return this;
}
}
}
class OrangeJuice extends Juice{
private int orangeSize;
private OrangeJuice(double density) {super(density);}
public static Builder builder(double density) {return new OrangeJuice(density).new Builder();}
public class Builder extends Juice.Builder<Builder> {
#Override
public OrangeJuice build() {return OrangeJuice.this;}
#Override
public Builder self() {return this;}
public Builder setOrangeSize(int orangeSize) {
OrangeJuice.this.orangeSize = orangeSize;
return this;
}
}
}
You have the line:
public FastFood testFood(FastFood.Builder fastFoodBuilder) {
But also:
abstract class FastFood extends Food {
...
public abstract class Builder<T extends Builder<T>> extends Food.Builder<T> {
There should be a warning saying that FastFood.Builder is a generic type.
The parameter type needs to be made appropriately generic. A wildcard will do (constraints will be inferred).
public FastFood testFood(FastFood.Builder<?> fastFoodBuilder) {
There are few problems with the code.
Class FastFood.Builder is generic: class Builder<T extends Builder<T>> extends Food.Builder<T>.
In the method FastFood testFood(FastFood.Builder fastFoodBuilder) the builder is declared without specifying the type T.
For example, AppleJuice specifies the parameter T type: class Builder extends Juice.Builder<Builder>.
The same problem will be present for Drink.Builder:
public static Drink testDrink(Drink.Builder drinkBuilder) {
return drinkBuilder
.setPrice(100) // returns Stuff.Builder
.setAlcoholVolume(0.7) //Error: cannot resolve method
.build();
}
To fix this, make method testFood generic accepting parameter FastFood.Builder<T>:
public static <T extends FastFood.Builder<T>> FastFood testFood(FastFood.Builder<T> fastFoodBuilder) {
return fastFoodBuilder.setMealType("fd").setHarm(1).setPrice(20).build();
}
The same is true for methods accepting Drink.Builder:
public static <T extends Drink.Builder<T>> Drink testDrink(Drink.Builder<T> drinkBuilder) {
return drinkBuilder.setPrice(100).setAlcoholVolume(0.7).build();
}
Usage example:
FastFood californiaPizza1 = testFood(CaliforniaPizza.builder(2));
In order to call addOlives(boolean) method and avoid casting from FastFood to a concrete class (e.g. CaliforniaPizza), it's more convenient to return builder from a method:
public static <T extends FastFood.Builder<T>> T testFoodBuilder(FastFood.Builder<T> fastFoodBuilder) {
return fastFoodBuilder.setMealType("fd").setHarm(1).setPrice(20);
}
And the usage:
CaliforniaPizza californiaPizza2 =
testFoodBuilder(CaliforniaPizza.builder(2))
.addOlives(true)
.build();
I've been looking to implement a Builder pattern with many subclasses and found this answer by #Josemy. However, it only has 1 level of subclasses: Pizza -> SomePizza. I tried to make use of the builder pattern for a more complex hierarchy only to fail dramatically.
I have the following class hierarchy:
Stuff
/ \
Food Drink
| |
Pizza Juice
/ \ / \
| SicilianPizza | OrangeJuice
| AppleJuice
CaliforniaPizza
Each class has a field(s) that should be set, some of those fields are obligatory (ob) and must be passed, some are optional (op) and can be set via a builder:
Stuff -> double price (op)
Food -> String mealType (op)
Pizza -> List<String> toppings (op), int size (ob)
CaliforniaPizza -> boolean addOlives (op)
SicilianPizza -> boolean addCheese (op)
Drink -> double density (ob), double alcoholVolume (op)
Juice -> String color (op)
AppleJuice -> String appleColor (op)
OrangeJuice -> int orangeSize (op)
I use builders to build the pizzas and drinks:
TestStuff.java:
public class TestStuff {
public static void main(String[] args) {
CaliforniaPizza californiaPizze = CaliforniaPizza.builder(2)
.addTopping("Tomatoes").addOlives(true).build();
SicilianPizza sicilianPizza = SicilianPizza.builder(1)
.addTopping("Bacon").addCheese(false).build();
AppleJuice appleJuice = AppleJuice.builder(40)
.setPrice(120).setAlcoholVolume(0).setAppleColor("yellow").build();
OrangeJuice orangeJuice = OrangeJuice.builder(35).setOrangeSize(8).build();
}
}
These are my classes:
Stuff.java:
public abstract class Stuff {
protected double price;
protected Stuff() {}
protected abstract class Builder<T extends Builder> {
protected abstract Stuff build();
protected abstract T self();
public T setPrice(double price) {
Stuff.this.price = price;
return (T) self();
}
}
}
Food.java:
public abstract class Food extends Stuff {
protected String mealType; //breakfast/dinner/etc
protected Food() {}
public abstract class Builder<T extends Builder> extends Stuff.Builder<Builder> {
protected abstract Food build();
protected abstract T self();
public T setMealType(String mealType) {
Food.this.mealType = mealType;
return (T) self();
}
}
}
Pizza.java:
public abstract class Pizza extends Food {
protected List<String> toppings = new ArrayList<>(); //optional
protected int size; //obligatory
protected Pizza(int size) {this.size = size;}
public abstract class Builder<T extends Builder> extends Food.Builder<Builder> {
public T addTopping(String topping) {
toppings.add(topping);
return (T) self();
}
}
}
CaliforniaPizza.java:
public class CaliforniaPizza extends Pizza {
private boolean addOlives;
private CaliforniaPizza(int size) {super(size);}
public static Builder builder(int size) {return new CaliforniaPizza(size).new Builder();}
public class Builder extends Pizza.Builder<Builder> {
#Override
public CaliforniaPizza build() {
return CaliforniaPizza.this;
}
#Override
public Builder self() {return this;}
public Builder addOlives(boolean addOlives) {
CaliforniaPizza.this.addOlives = addOlives;
return this;
}
}
}
SicilianPizza.java:
public class SicilianPizza extends Pizza {
private boolean addCheese;
private SicilianPizza(int size) {super(size);}
public static Builder builder(int size) {
return new SicilianPizza(size).new Builder();
}
public class Builder extends Pizza.Builder<Builder> {
#Override
public SicilianPizza build() {return SicilianPizza.this;}
#Override
public Builder self() {return this;}
public Builder addCheese(boolean addCheese) {
SicilianPizza.this.addCheese = addCheese;
return this;
}
}
}
Drink.java:
public abstract class Drink extends Stuff {
protected double density;
protected double alcoholVolume;
protected Drink(double density) {this.density = density;}
public abstract class Builder<T extends Builder> extends Stuff.Builder<Builder> {
protected abstract Drink build();
protected abstract T self();
public T setAlcoholVolume(double alcoholVolume) {
Drink.this.alcoholVolume = alcoholVolume;
return (T) self();
}
}
}
Juice.java:
public abstract class Juice extends Drink {
private String color;
protected Juice(double density) {super(density);}
public abstract class Builder<T extends Builder> extends Drink.Builder<Builder> {
public Builder setColor(String color) {
Juice.this.color = color;
return (T) self();
}
}
}
AppleJuice.java:
public class AppleJuice extends Juice {
private String appleColor;
private AppleJuice(double density) {super(density);}
public static Builder builder(double density) {return new AppleJuice(density).new Builder();}
public class Builder extends Juice.Builder<Builder> {
#Override
public AppleJuice build() {
return AppleJuice.this;
}
#Override
public Builder self() {
return this;
}
public Builder setAppleColor(String appleColor) {
AppleJuice.this.appleColor = appleColor;
return this;
}
}
}
OrangeJuice.java:
public class OrangeJuice extends Juice{
private int orangeSize;
private OrangeJuice(double density) {super(density);}
public static Builder builder(double density) {return new OrangeJuice(density).new Builder();}
public class Builder extends Juice.Builder<Builder> {
#Override
public OrangeJuice build() {return OrangeJuice.this;}
#Override
public Builder self() {return this;}
public Builder setOrangeSize(int orangeSize) {
OrangeJuice.this.orangeSize = orangeSize;
return this;
}
}
}
I have several problems with this code:
#Override annotations above build() and self inside CaliforniaPizza and SicilianPizza are highlighted in Idea Intellij and the error message says that method does not override method from its super class. While there are no such errors inside Juice child classes.
This line inside TestStuff:
AppleJuice appleJuice = AppleJuice.builder(40)
.setPrice(120).setAlcoholVolume(0)
.setAppleColor("yellow").build();
gives error: setAppleColor is highlited with red and can't be resolved. The reason is that setAlcoholVolume returns a Drink.Builder instead of AppleJuice.Builder. At the same time if I were to call setAlcoholVolume right after builder() method it would return Juice.Builder:
Although there are problems with juices builders, at least I can somewhat access all the methods (at least if I call them right after builder(). However with foods my options are limited:
There's not Food's setMealType there.
I have a return (T) self() line inside setters in Juice and Pizza. However, only inside Juice do I get the warning about unchecked cast:
while in Pizza I get no warning at all.
I have suspicions that at least problems 2 and 3 have to do with type erasure. An AppleJuice.Builder gets passed to Juice.Builder as T, but then the Juice.Builder<AppleJuice.Builder> gets passed to Drink.Builder<Juice.Builder> and the information about AppleJuice gets lost. Then Drink.Builder<Juice.Builder> gets passed to Stuff.Builder<Drink.Builder> and only information about Drink.Builder is retained. Thus when I call setPrice(120) right away, it returns Drink.Builder instead of Juice.Builder. If I'm correct, what is the way to fix it? And what are the reasons behind another issues I've encountered?
The cast
protected abstract T self();
public T setPrice(double price) {
Stuff.this.price = price;
return (T) self();
}
self() already returns T. The cast is unnecessary.
Unresolved methods
public abstract class Stuff {
// ...
protected abstract class Builder<T extends Builder> {
Builder is a generic type, but it's used a raw type here. It needs the type parameter, like java.lang.Enum.
protected abstract class Builder<T extends Builder<T>> {
Again in subtypes.
public abstract class Food extends Stuff {
// ...
public abstract class Builder<T extends Builder> extends Stuff.Builder<Builder>
Also here we want to parameterise StuffBuilder with T.
public abstract class Builder<T extends Builder> extends Stuff.Builder<T>
The #Overrides
With the types sorted, the override problems should disappear.
Edit:
So I've been through and made the changes to the code. (Added package, import and removed public from classes to make compilable in one blob.) Eclipse is happy with it, except for the unused variables.
package pizza;
import java.util.*;
class TestStuff {
public static void main(String[] args) {
CaliforniaPizza californiaPizze = CaliforniaPizza.builder(2)
.addTopping("Tomatoes").addOlives(true).build();
SicilianPizza sicilianPizza = SicilianPizza.builder(1)
.addTopping("Bacon").addCheese(false).build();
AppleJuice appleJuice = AppleJuice.builder(40)
.setPrice(120).setAlcoholVolume(0).setAppleColor("yellow").build();
OrangeJuice orangeJuice = OrangeJuice.builder(35).setOrangeSize(8).build();
}
}
abstract class Stuff {
protected double price;
protected Stuff() {}
protected abstract class Builder<T extends Builder<T>> {
protected abstract Stuff build();
protected abstract T self();
public T setPrice(double price) {
Stuff.this.price = price;
return self();
}
}
}
abstract class Food extends Stuff {
protected String mealType; //breakfast/dinner/etc
protected Food() {}
public abstract class Builder<T extends Builder<T>> extends Stuff.Builder<T> {
protected abstract Food build();
protected abstract T self();
public T setMealType(String mealType) {
Food.this.mealType = mealType;
return self();
}
}
}
abstract class Pizza extends Food {
protected List<String> toppings = new ArrayList<>(); //optional
protected int size; //obligatory
protected Pizza(int size) {this.size = size;}
public abstract class Builder<T extends Builder<T>> extends Food.Builder<T> {
public T addTopping(String topping) {
toppings.add(topping);
return self();
}
}
}
class CaliforniaPizza extends Pizza {
private boolean addOlives;
private CaliforniaPizza(int size) {super(size);}
public static Builder builder(int size) {return new CaliforniaPizza(size).new Builder();}
public class Builder extends Pizza.Builder<Builder> {
#Override
public CaliforniaPizza build() {
return CaliforniaPizza.this;
}
#Override
public Builder self() {return this;}
public Builder addOlives(boolean addOlives) {
CaliforniaPizza.this.addOlives = addOlives;
return this;
}
}
}
class SicilianPizza extends Pizza {
private boolean addCheese;
private SicilianPizza(int size) {super(size);}
public static Builder builder(int size) {
return new SicilianPizza(size).new Builder();
}
public class Builder extends Pizza.Builder<Builder> {
#Override
public SicilianPizza build() {return SicilianPizza.this;}
#Override
public Builder self() {return this;}
public Builder addCheese(boolean addCheese) {
SicilianPizza.this.addCheese = addCheese;
return this;
}
}
}
abstract class Drink extends Stuff {
protected double density;
protected double alcoholVolume;
protected Drink(double density) {this.density = density;}
public abstract class Builder<T extends Builder<T>> extends Stuff.Builder<T> {
protected abstract Drink build();
protected abstract T self();
public T setAlcoholVolume(double alcoholVolume) {
Drink.this.alcoholVolume = alcoholVolume;
return (T) self();
}
}
}
abstract class Juice extends Drink {
private String color;
protected Juice(double density) {super(density);}
public abstract class Builder<T extends Builder<T>> extends Drink.Builder<T> {
public Builder<T> setColor(String color) {
Juice.this.color = color;
return self();
}
}
}
class AppleJuice extends Juice {
private String appleColor;
private AppleJuice(double density) {super(density);}
public static Builder builder(double density) {return new AppleJuice(density).new Builder();}
public class Builder extends Juice.Builder<Builder> {
#Override
public AppleJuice build() {
return AppleJuice.this;
}
#Override
public Builder self() {
return this;
}
public Builder setAppleColor(String appleColor) {
AppleJuice.this.appleColor = appleColor;
return this;
}
}
}
class OrangeJuice extends Juice{
private int orangeSize;
private OrangeJuice(double density) {super(density);}
public static Builder builder(double density) {return new OrangeJuice(density).new Builder();}
public class Builder extends Juice.Builder<Builder> {
#Override
public OrangeJuice build() {return OrangeJuice.this;}
#Override
public Builder self() {return this;}
public Builder setOrangeSize(int orangeSize) {
OrangeJuice.this.orangeSize = orangeSize;
return this;
}
}
}
I am trying to understand and accomplish the task of trying to create a class that extends a generic class that accepts all types of classes. So far I have that working. I am trying to create a class that extends a generic holder class and have this class accept only specific objects.
Example, a class called "ComputerOrder" that will not accept an Apple or Orange object but only a ComputerPart or Peripheral object, such as Motherboard or Printer objects. Been stuck on this for 2 weeks. I can't for the life of me figure this concept out. Any help would be appreciated.
abstract class Product{
protected float price;
abstract float price();
public String toString() {
return "Price = " + String.valueOf(price) + " ";
}
}
class Apple extends Product{}
class Orange extends Product{}
class ComputerPart extends Product{
public ComputerPart(float p){
price = p;
}
public float price() {
return price;
}
}
class Motherboard extends ComputerPart{
protected String manufacturer;
public Motherboard(String mfg, float p) {
super(p);
manufacturer = mfg;
}
public String getManufacturer() {
return manufacturer;
}
}
class Peripheral extends Product{
public Peripheral(float p) {
price = p;
}
public float price() {
return price;
}
}
class Printer extends Peripheral{
protected String model;
public Printer(String model, float p) {
super(p);
this.model = model;
}
public String getModel() {
return model;
}
}
class Cheese extends Product{
public Cheese(float p) {
price = p;
}
public float price() {
return price;
}
}
class Cheddar extends Cheese{
public Cheddar(float p) {
super(p);
}
}
class GenericOrder<T>{
public ArrayList<T> storage = new ArrayList<T>();
private static int counter = 1;
public final int id;
public T obj;
public GenericOrder(){
id = counter;
counter++;
}
public void add(T item){
storage.add(item);
}
public T get(int in){
return obj;
}
public void getId(){
System.out.println(this.id);
}
public String toString(){
String ret = "";
Iterator<T> it = storage.iterator();
while (it.hasNext()) {
ret += it.next() + "\n";
}
return ret;
}
}
class ComputerOrder extends GenericOrder {
public void add(ComputerPart in){
if(in instanceof ComputerPart){
storage.add(in);
}
}
}
public class Tme2{
public static void main(String[] args){
ComputerOrder com = new ComputerOrder();
com.add(new Motherboard("bla", 3.33f))
}
}
You can do it like this:
class ComputerOrder<T extends ComputerProduct> extends GenericOrder<T> {
//...
}
Here, ComputerProduct is a class that extends Product and all your computer products like ComputerPart or Peripheral extend ComputerProduct. Similarly, you could create a class FoodProduct derived from Product, from which Apple, Orange and Cheese are derived:
class FoodOrder<T extends FoodProduct> extends GenericOrder<T> {
//...
}
The declaration <T extends ComputerProduct> is a type restriction, which ensures that all types of T are derived from ComputerPart, otherwise you will get a compiler error.
The ComputerOrder class is still generic, so you could instance an order for all computer products:
ComputerOrder order = new ComputerOrder<ComputerProduct>();
// Add peripherals, printers, motherboards...
// Apples, ... will throw compiler errors...
But you could also restrict it to peripherals only:
ComputerOrder order = new ComputerOrder<Peripheral>();
// Add peripherals, printers,...
// Apples, motherboards (ComputerProduct, but NOT Peripheral) will fail...
I have the following example in Java:
public abstract class Vehicle {
private final String name;
private final String make;
public Vehicle(final String name, final String make) {
this.make = make;
this.name = name;
}
}
public final class Car extends Vehicle {
public Car(final String name, final String make) {
super(name, make);
}
}
public final class Truck extends Vehicle {
final Integer grossVehicleWeight;
public Truck(final String name, final String make, final Integer gvw) {
super(name, make);
this.grossVehicleWeight = gvw;
}
Say I want to do some work with a vehicle, and the work is not dependent on the subclass of vehicle. So, I have a method in another class like this:
public void doStuff(public final Vehicle vehicle) {
//do stuff here
//then insert it into my database:
insertVehicle(vehicle);
}
However, I want to do different things in my insertVehicle, so I override that method for each subclass:
public void insertVehicle(Car car) { //do stuff for a car }
public void insertVehicle(Truck truck) { //do stuff for a truck }
In my doStuff method, I could use instanceOf to determine the class of the vehicle (Car or Truck), and then cast the vehicle into that class and call the insertVehicle method like this:
public void doStuff(public final Vehicle vehicle) {
//do stuff here
//then insert it into my database:
if (vehicle instanceof Car) {
insertVehicle((Car) vehicle);
} else {
insertVehicle((truck) vehicle);
}
}
However, I have read that using instanceof is not the best way to do this. 1
How could I best rework this so that I do not have to use instanceof?
You can use the Visitor Pattern:
public interface VehicleVisitor {
public void visit(Car car);
public void visit(Truck truck);
}
public class Car extends Vehicle {
#Override
public void insert(VehicleVisitor vehicleVisitor) {
vehicleVisitor.visit(this);
}
}
public class Truck extends Vehicle {
#Override
public void insert(VehicleVisitor vehicleVisitor) {
vehicleVisitor.visit(this);
}
}
public abstract class Vehicle {
public abstract void insert(VehicleVisitor vehicleVisitor);
}
public class VehicleVisitorImpl implements VehicleVisitor {
#Override
public void visit(Car car) {
System.out.println("insert car");
}
#Override
public void visit(Truck truck) {
System.out.println("insert truck");
}
}
public class Main {
public static void main(String[] args) {
Vehicle vehicle = new Car();
// finally the agnostic call
vehicle.insert(new VehicleVisitorImpl());
}
}
You can make the abstract function inside the vehicle for
public abstract void doStuff()
call this function from the instance of the object that you want to modify
ford.doStuff(); //ford is car instance
and then you can make modification using this.
doStuff()
{
this.cost += 10;
}
Otherwise, you can add a variable for vehicle which indicated what is the vehicle type and return it. Like:
public void doStuff(public final Vehicle vehicle) {
//do stuff here
//then insert it into my database:
if (vehicle.getType()== 'Car') {
insertVehicle((Car) vehicle);
} else {
insertVehicle((truck) vehicle);
}
}
This variable 'vehicleType' will be in the vehicle class and will be initialized inside the constructor:
public final class Car extends Vehicle {
public Car(final String name, final String make, final String vehicleType) {
super(name, make, type);
}
}
It depends on what kind of problem you trying to solve. If it is persistency make sure you are not reinventing JPA. If it is type-specific processing then you can solve it as #denis suggested. Or if you want to keep entities in POJO-style you can use strategy pattern like:
Map<Class<?>, Consumer<Vehicle>> consumers;
{
consumers.put(Car.class, v -> insertVehicle((Car)v));
consumers.put(Truck.class, v -> insertVehicle((Truck)v));
}
public void doStuff(public final Vehicle vehicle) {
//do stuff here
consumers
.get(vehicle.getClass())
.accept(vehicle);
}
One way is to have the insertVehicle() method abstract in Vehicle. And then implement them in the subclasses Car and Truck.
However, this moves the logic into the POJOs. Maybe it is better to separate db-logic from the POJOs, i.e. just use instanceof in this case.
public abstract class Vehicle {
private final String name;
private final String make;
public Vehicle(final String name, final String make) {
this.make = make;
this.name = name;
}
public abstract void insertVehicle();
}
public final class Car extends Vehicle {
public Car(final String name, final String make) {
super(name, make);
}
public void insertVehicle() {
}
}
public final class Truck extends Vehicle {
final Integer grossVehicleWeight;
public Truck(final String name, final String make, final Integer gvw) {
super(name, make);
this.grossVehicleWeight = gvw;
}
public void insertVehicle() {
}
}
public void doStuff(Vehicle vehicle) {
//do stuff here
//then insert it into my database:
vehicle.insertVehicle();
}
If you don't like putting doStuff() into Car and Truck, you could have a doStuff() method for each of them, and put the common Vehicle logic into another method.
private void doCommonStuff(final Vehicle vehicle) {
//do stuff here
}
public void doStuff(final Car car) {
doCommonStuff(car);
//then insert it into my database:
insertVehicle(car);
}
public void doStuff(final Truck truck) {
doCommonStuff(truck);
//then insert it into my database:
insertVehicle(truck);
}
We can do better, though, with generics.
public abstract class StuffDoer<T extends Vehicle> {
public void doStuff(final T vehicle) {
// do stuff here
// then insert it into my database:
insertVehicle(vehicle);
}
public abstract void insertVehicle(T vehicle);
}
public class CarStuffDoer extends StuffDoer<Car> {
public void insertVehicle(Car car) {
// whatever
}
}
public class TruckStuffDoer extends StuffDoer<Truck> {
public void insertVehicle(Truck truck) {
// whatever else
}
}