I'm familiar with using the builder pattern with generics and subclassing, but I can't see how to make it work with a non-trivial tree of subclasses (i.e. C extends B extends A). Here's a simple example of what I'm trying to do:
class A {
private final int value;
protected A(ABuilder builder) {
this.value = builder.value;
}
public int getValue() { return value; }
public static class ABuilder<T extends ABuilder<T>> {
private int value;
public T withValue(int value) {
this.value = value;
return (T) this;
}
public A build() {
return new A(this);
}
}
}
class B extends A {
private final String name;
public static BBuilder builder() {
return new BBuilder();
}
protected B(BBuilder builder) {
super(builder);
this.name = builder.name;
}
public String getName() { return name; }
public static class BBuilder<U extends BBuilder<U>> extends ABuilder<BBuilder<U>> {
private String name;
public U withName(String name) {
this.name = name;
return (U) this;
}
public B build() {
return new B(this);
}
}
}
Everything is fine if I declare BBuilder without the generic type:
public static class BBuilder extends ABuilder<BBuilder>
Since I want BBuilder to be extended by a CBuilder, I'm trying to use the same sort of Curiously Recurring Template Pattern as ABuilder. But like this, the compiler sees BBuilder.withValue() as returning an ABuilder, not a BBuilder as I want. This:
B b = builder.withValue(1)
.withName("X")
.build();
doesn't compile. Can anyone see what I'm doing wrong here, I've been going round trying different patterns of generics but can't get it to work.
Thanks to anyone who has any advice.
It seems that your mistake only with declaring correct parameter:
class A {
private final int value;
public static <T extends Builder<T>> T builderA() {
return (T)new Builder<>();
}
protected A(Builder<? extends Builder<?>> builder) {
value = builder.value;
}
public static class Builder<T extends Builder<T>> {
private int value;
public T withValue(int value) {
this.value = value;
return (T)this;
}
public A build() {
return new A(this);
}
}
}
class B extends A {
private final String name;
public static <T extends Builder<T>> T builderB() {
return (T)new Builder<>();
}
protected B(Builder<? extends Builder<?>> builder) {
super(builder);
name = builder.name;
}
public static class Builder<T extends Builder<T>> extends A.Builder<T> {
private String name;
public Builder<T> withName(String name) {
this.name = name;
return this;
}
public B build() {
return new B(this);
}
}
}
Client code:
A a = A.builder().withValue(1).build();
B b = B.builder().withValue(2).withName("xx").build();
Are you certain you need generics? This hierarchy seems to work fine without generics.
static class A {
protected final int value;
protected A(ABuilder builder) {
this.value = builder.value;
}
public int getValue() {
return value;
}
#Override
public String toString() {
return "A{" +
"value=" + value +
'}';
}
public static ABuilder builder() {
return new ABuilder();
}
public static class ABuilder {
protected int value;
public ABuilder withValue(int value) {
this.value = value;
return this;
}
public A build() {
return new A(this);
}
}
}
static class B extends A {
protected final String name;
protected B(BBuilder builder) {
super(builder);
this.name = builder.name;
}
public String getName() {
return name;
}
#Override
public String toString() {
return "B{" +
"value=" + value +
", name='" + name + '\'' +
'}';
}
public static BBuilder builder() {
return new BBuilder();
}
public static class BBuilder extends ABuilder {
private String name;
public BBuilder withName(String name) {
this.name = name;
return this;
}
#Override
public BBuilder withValue(int value) {
this.value = value * 2;
return this;
}
public B build() {
return new B(this);
}
}
}
static class C extends B {
private final String otherName;
protected C(CBuilder builder) {
super(builder);
this.otherName = builder.otherName;
}
public String getName() {
return otherName;
}
#Override
public String toString() {
return "C{" +
"value=" + value +
", name='" + name + '\'' +
", otherName='" + otherName + '\'' +
'}';
}
public static CBuilder builder() {
return new CBuilder();
}
public static class CBuilder extends BBuilder {
private String otherName;
public CBuilder withName(String name) {
this.otherName = name;
return this;
}
public C build() {
return new C(this);
}
}
}
public void test() {
A a = A.builder().withValue(10).build();
B b = B.builder().withValue(10).withName("B").build();
C c = C.builder().withName("C").build();
System.out.println("a = "+a);
System.out.println("b = "+b);
System.out.println("c = "+c);
}
Related
I get Null Pointer Exception when running the code below:
public class Engine{
private String name = null;
private Mercedes m = null;
private Engine() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Mercedes getM() {
return m;
}
public void setM(Mercedes m) {
this.m = m;
}
public static EngineBuilder builder() {
return new EngineBuilder();
}
public static class EngineBuilder {
private Engine e = null;
public EngineBuilder builder() {
e = new Engine();
return this;
}
public Engine build() {
return this.e;
}
public EngineBuilder setName(String name) {
this.e.setName(name);
return this;
}
public EngineBuilder setM(Mercedes m) {
this.e.setM(m);
return this;
}
}
public static void main(String[] args) {
EngineBuilder builder = Engine.builder();
builder.setName("test");
Engine e = builder.build();
}
}
}
I expected the Builder pattern would work, but I got
"Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Engine.setName(String)" because "this.e" is null"
In your code EngineBuilder class have only default constructor, Which does not initialize Engine Object. Write a constructor which initializes required objects.
The attribute Engine e in EngineBuilder is initialized only in its builder() method which is never called. There's multiple ways to fix your code although I'd implement the builder pattern in a different way:
public class Engine {
private final String name;
private Engine(Builder builder) {
this.name = builder.name;
}
public String getName() {
return name;
}
public static Builder builder() {
return new Builder();
}
#Override
public String toString() {
return name;
}
public static class Builder {
private String name;
private Builder() {
}
public Builder name(String name) {
this.name = name;
return this;
}
public Engine build() {
return new Engine(this);
}
}
public static void main(String[] args) {
Engine mercedes = Engine.builder().name("Mercedes").build();
System.out.println(mercedes); // Mercedes
}
}
If you want the instances of the Engine class to act as traditional POJOs then remove final from its attributes and add a public default constructor and setters to it.
you need add constructor in builder.
public class Engine{
private String name = null;
private Mercedes m = null;
private Engine() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Mercedes getM() {
return m;
}
public void setM(Mercedes m) {
this.m = m;
}
public static EngineBuilder builder() {
return new EngineBuilder();
}
public static class EngineBuilder {
private Engine e = null;
// you need add Constructor in Builder
public EngineBuilder(){
e = new Engine();
}
public EngineBuilder builder() {
e = new Engine();
return this;
}
public Engine build() {
return this.e;
}
public EngineBuilder setName(String name) {
this.e.setName(name);
return this;
}
public EngineBuilder setM(Mercedes m) {
this.e.setM(m);
return this;
}
}
public static void main(String[] args) {
EngineBuilder builder = Engine.builder();
builder.setName("test");
Engine e = builder.build();
}
}
I have a class called BalanceTarget
public class BalanceTarget {
#JsonProperty("amount")
private String amount;
public enum CreditDebitIndicatorEnum {
CREDIT("Credit"),
DEBIT("Debit");
private String value;
CreditDebitIndicatorEnum(String value) {
this.value = value;
}
#JsonValue
public String getValue() {
return value;
}
#Override
public String toString() {
return String.valueOf(value);
}
#JsonCreator
public static CreditDebitIndicatorEnum fromValue(String value) {
for (CreditDebitIndicatorEnum b : CreditDebitIndicatorEnum.values()) {
if (b.value.equals(value)) {
return b;
}
}
throw new IllegalArgumentException("Unexpected value '" + value + "'");
}
}
#JsonProperty("creditDebitIndicator")
private CreditDebitIndicatorEnum creditDebitIndicator;
//getter and setters
}
Another class BalanceSource
public class BalanceSource {
#JsonProperty("amount")
private String amount;
public enum CreditDebitIndicatorEnum {
C("C"),
D("D");
private String value;
CreditDebitIndicatorEnum(String value) {
this.value = value;
}
#JsonValue
public String getValue() {
return value;
}
#Override
public String toString() {
return String.valueOf(value);
}
#JsonCreator
public static CreditDebitIndicatorEnum fromValue(String value) {
for (CreditDebitIndicatorEnum b : CreditDebitIndicatorEnum.values()) {
if (b.value.equals(value)) {
return b;
}
}
throw new IllegalArgumentException("Unexpected value '" + value + "'");
}
}
#JsonProperty("creditDebitIndicator")
private CreditDebitIndicatorEnum creditDebitIndicator;
//getter and setters
}
I have a mapper for Balance
#Mapper(componentModel = "spring", uses = CreditDebitIndicatorMapper.class)
public interface BalanceMapper {
BalanceTarget convert(BalanceSource a);
}
I have another mapper for CreditDebitIndicator
#Mapper(componentModel = "spring")
public interface CreditDebitIndicatorMapper {
#ValueMapping(source = "C", target = "CREDIT")
#ValueMapping(source = "D", target = "DEBIT")
BalanceTarget.CreditDebitIndicatorEnum convert(BalanceSource.CreditDebitIndicatorEnum a);
}
On building the code it gives out this error
The following constants from the property
"BalanceSource.CreditDebitIndicatorEnum
balances[].creditDebitIndicator" enum have no corresponding constant
in the "BalanceTarget.CreditDebitIndicatorEnum
balances[].creditDebitIndicator" enum and must be be mapped via adding
additional mappings: C, D.
I'm busy implementing Step Builders into a Java application and I've written some horrendous code. I'm quite certain I'm missing a necessary step.
For an example, I'll use the buildable class Machine.java
public class Machine {
private String type;;
private boolean mobile;
private final String mobileType;
public Machine(MachineBuilder builder) {
this.type = builder.type;
this.mobile = builder.mobile;
this.mobileType = builder.mobileType;
}
public String getType() { return this.type; }
public boolean getMobile() { return this.mobile; }
public String getMobileType() { return this.mobileType; }
}
And the step builder for it as MachineBuilder.java
public class MachineBuilder {
public String type;
public boolean mobile;
public String mobileType;
public MachineBuilder() { }
// initialize builder
public Builder start() {
return new Builder();
}
// interfaces
public interface iType {
iBuild withType(String type);
}
public interface iMobileType {
iBuild withMobileType(String mobileType);
}
public interface iBuild {
iMobileType withMobile();
iBuild withMobileType(String mobileType);
Machine build();
}
// subclass to return
public static class Builder extends MachineBuilder implements iType, iMobileType, iBuild {
public iBuild withType(String type) {
super.type = type; return this;
}
public iMobileType withMobile() {
super.mobile = true; return this;
}
public iBuild withMobileType(String mobileType) {
super.mobileType = mobileType; return this;
}
public Machine build() {
return new Machine(this);
}
}
}
The intention is to have type as a required step, then mobile as optional but if mobile is used then mobileType must be used as well.
It's only half working though
// fine
Machine car = new MachineBuilder()
.start().withType("car").withMobile().withMobileType("driving").build();
System.out.println(car.getType() + ":" + car.getMobile() + ":" + car.getMobileType());
// fine
Machine washingMachine = new MachineBuilder()
.start().withType("washingMachine").build();
System.out.println(washingMachine.getType() + ":" + washingMachine.getMobile() + ":" + washingMachine.getMobileType());
// corrupt (no type)
Machine boat = new MachineBuilder()
.start().withMobile().withMobileType("sailing").build();
System.out.println(boat.getType() + ":" + boat.getMobile() + ":" + boat.getMobileType());
// corrupt (no anything)
Machine bicycle = new MachineBuilder()
.start().build();
System.out.println(bicycle.getType() + ":" + bicycle.getMobile() + ":" + bicycle.getMobileType());
I had to initialize the builder object with the method start but this is not implementing any of the interfaces so just calling start then build will corrupt the object. Similarly calling the optional method for mobile allows it to bypass the type.
Is it possible to force flow direction from the start without using a start method at all? I feel like I am missing something very stupid.
PS. sorry for slapping so much code into the question I just wanted to illustrate the issue as best as I can
Thrill to answer this question. I try to rewrite your code. Just reorganize it following Step Builder Pattern's strategy.
Add no description here, hope you can easily understand the code.
class Machine {
private String type;
private boolean isMobile;
private String mobileType;
public static TypeStep builder(){
return new MachineBuilder();
}
public interface TypeStep{
IsMobileStep withType(String type);
}
public interface IsMobileStep{
MobileTypeStep withMobile(boolean isMobile);
}
public interface MobileTypeStep{
Build withMobileType(String mobileType);
}
public interface Build{
Machine build();
}
public static class MachineBuilder implements TypeStep, IsMobileStep, MobileTypeStep, Build {
private String type;
private boolean isMobile;
private String mobileType;
#Override
public IsMobileStep withType(String type) {
this.type = type;
return this;
}
#Override
public MobileTypeStep withMobile(boolean isMobile) {
this.isMobile = isMobile;
return this;
}
#Override
public Build withMobileType(String mobileType) {
this.mobileType = mobileType;
return this;
}
#Override
public Machine build() {
return new Machine(this);
}
}
private Machine(MachineBuilder machineBuilder) {
this.type = machineBuilder.type;
this.isMobile = machineBuilder.isMobile;
this.mobileType = machineBuilder.mobileType;
}
public String getType() {
return type;
}
public boolean isMobile() {
return isMobile;
}
public String getMobileType() {
return mobileType;
}
}
Test run:
public class Main{
public static void main(String[] args) {
Machine car = Machine.builder().withType("car").withMobile(true).withMobileType("driving").build();
System.out.println("Model 1:"+ car.getType() +":"+ car.isMobile()+":"+car.getMobileType());
Machine boat = Machine.builder().withType("boat").withMobile(true).withMobileType("driving").build();
System.out.println("Model 2:"+ boat.getType() +":"+ boat.isMobile()+":"+boat.getMobileType());
}
}
Output:
Model 1:car:true:driving
Model 2:boat:true:driving
For better readability: Github Repo Step Builder
I think your builder implementation is very difficult and not very correct.
The typical builder should have private constructors, initial methods, field setters and build methods. I would do so:
public class MachineBuilder {
public String type;
public boolean mobile;
public String mobileType;
private MachineBuilder (final String type, final boolean mobile, final String mobileType) {
this.type = type;
this.mobile = mobile;
this.mobileType = mobileType;
}
private MachineBuilder(){
}
public MachineBuilder setType(final String source) {
this.type = source;
return this;
}
public MachineBuilder setMobile(final boolean source) {
this.mobile = source;
return this;
}
public MachineBuilder setMobileType(final String source) {
this.mobileType = source;
return this;
}
public static MachineBuilder init() {
return new MachineBuilder();
}
public static MachineBuilder init(final String type, final boolean mobile, final String mobileType) {
return new MachineBuilder(type, mobile, mobileType);
}
public MachineBuilder build() {
return Machine(this.type, this.mobile, this.mobileType);
}
}
I have a Class A with name and value attributes.
public class A {
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
I have another Class B, such as
public class B {
private String attribute01;
private String attribute01;
private String attribute01;
public String getAttribute01() {
return attribute01;
}
public void setAttribute01(String name) {
this.attribute01 = name;
}
...
}
I would like to return a list with A type, having attribute01 key and where value is getAttribute01() from B, such as ({attribute01, getAttribute01()},{attribute02, getAttribute02()}).
How to implement it?.
Thanks in advance.
Actually I can use a very stupid way, such as
public List<A> keyvalueList(final B objB) {
List<A> list = new ArrayList<>();
A objA = new A();
objA.setName("attribute01");
objA.setValue(objB.getAttribute01());
list.add(objA);
objA = new A();
objA.setName("attribute02");
objA.setValue(objB.getAttribute02());
list.add(objA);
...
return list;
}
Part of them hard coding, obvious it is not a smart way, any proposal.
I wrote sample code for List.Please check my code that is ok to use or not.I added another extra class C.in C,it has two attribute String nameFromA and String attFromB.You should add this C object to list.Following is sample code.
public class A {
private String name;
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
public class B {
private String att1;
private String att2;
private String att3;
public String getAtt1() {
return att1;
}
public void setAtt1(String att1) {
this.att1 = att1;
}
public String getAtt2() {
return att2;
}
public void setAtt2(String att2) {
this.att2 = att2;
}
public String getAtt3() {
return att3;
}
public void setAtt3(String att3) {
this.att3 = att3;
}
}
public class C {
private String namefromA;
private String attfromB;
public String getNamefromA() {
return namefromA;
}
public void setNamefromA(String namefromA) {
this.namefromA = namefromA;
}
public String getAttfromB() {
return attfromB;
}
public void setAttfromB(String attfromB) {
this.attfromB = attfromB;
}
}
public class Test {
public static void main(String args[]){
C c = new C();
A a = new A();
B b = new B();
a.setName("A1");
b.setAtt1("100");
c.setNamefromA(a.getName());
c.setAttfromB(b.getAtt1());
List list = new ArrayList();
//use generic
list.add(c);
}
}
if you don't want to use class C,then you can use Test class like that
import java.util.ArrayList;
import java.util.List;
public class Test {
private String nameFromA;
private String valueFromB;
public Test(String nameFromA, String valueFromB) {
super();
this.nameFromA = nameFromA;
this.valueFromB = valueFromB;
}
public static void main(String args[]){
A a = new A();
B b = new B();
a.setName("A1");
b.setAtt1("100");
Test test = new Test(a.getName(),b.getAtt1());
List list = new ArrayList();
list.add(test);
}
}
This is my opinion only.Please check it is ok or not.
I'm trying to obfuscate a parcelable class with Proguard:
Before adding the Parcelable part the class is:
public class Foo{
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
The obfuscated result is:
public class a
{
private String a;
public String a()
{
return this.a;
}
public void a(String paramString)
{
this.a = paramString;
}
}
After adding implementing parcelable the example class is
public class Foo implements Parcelable {
private String value;
private Foo(Parcel in) {
value = in.readString();
}
public Foo() {
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(value);
}
public static final Parcelable.Creator<Foo> CREATOR
= new Parcelable.Creator<Foo>() {
public Foo createFromParcel(Parcel in) {
return new Foo(in);
}
public Foo[] newArray(int size) {
return new Foo[size];
}
};
}
The obfuscated result is
public class Foo implements Parcelable {
public static final Parcelable.Creator CREATOR = new a();
private String a;
public Foo() {
}
private Foo(Parcel paramParcel) {
this.a = paramParcel.readString();
}
public String a() {
return this.a;
}
public void a(String paramString) {
this.a = paramString;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel paramParcel, int paramInt) {
paramParcel.writeString(this.a);
}
}
class a implements Parcelable.Creator {
public Foo a(Parcel paramParcel) {
return new Foo(paramParcel, null);
}
public Foo[] a(int paramInt) {
return new Foo[paramInt];
}
}
How can I configure proguard for obfuscate the whole class (including name, params and methods) except the parcelable part?
Thanks
Try putting this in your proguard.cfg file:
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
That should preserve Parcelable part and obfuscate everything else.