Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bulider for abstract class should not implement build() method #99

Open
damianszczepanik opened this issue May 23, 2015 · 10 comments
Open

Comments

@damianszczepanik
Copy link

Case is similar to #98 but this time I want to have full support for Dog builder which should also have access to Animal attributes.

To make it happen I declared builder for Animal (AnimalBuilder) and for Dog I declared following builder:

@GeneratePojoBuilder(withBaseclass = AnimalBuilder.class)

but because Animal s abstract following build() method in Animal class won't compile:

  public AbstractVersionableEntity build() {
    try {
      Animal result = new Animal(); // <-- error, Animal is abstract
      if (isSet$id$java$lang$Long) {
        result.setId(value$id$java$lang$Long);
      }
      return result;
    } catch (RuntimeException ex) {
      throw ex;
    } catch (Exception ex) {
      throw new java.lang.reflect.UndeclaredThrowableException(ex);
    }
  }

So the simplest way is to not create build() method for abstract classes. It has no sense and it won't compile.

@mkarneim
Copy link
Owner

The generated DogBuilder already has full support for all Animal attributes.

You do not need to make DogBuilder an extension of AnimalBuilder.

Example:

@GeneratePojoBuilder
public class Animal {
  public Date dateOfBirth; 
}

@GeneratePojoBuilder
public class Dog extends Animal {
  public String name;
}
Dog dog = new DogBuilder().withName("Lassie").withDateOfBirth( new Date()).build();

@mkarneim
Copy link
Owner

Btw, if Animal is abstract, just remove the annotation from Animal:

Example:

public abstract class Animal {
  public Date dateOfBirth; 
}

@GeneratePojoBuilder
public class Dog extends Animal {
  public String name;
}

You still have full support of all Animal properties in DogBuilder:

Dog dog = new DogBuilder().withName("Lassie").withDateOfBirth( new Date()).build();

@damianszczepanik
Copy link
Author

That makes sense but when I was trying to annotate like this I found that DogBuilder does not have withDateOfBirth and it does not extend Animal. Will check it again.

Does DogBuilder implement withDateOfBrith out of the box or some flag needs to be set?

@mkarneim
Copy link
Owner

The DogBuilder will not extend any other builder.
But it will have with-methods for all accessible properties of the produced pojo, regardless of the point of declaration in the pojo's class hierarchy,

@damianszczepanik
Copy link
Author

OK, finally I found that several fields have protected access and we use org.springframework.test.util.ReflectionTestUtils;.setField(dog, "id", id); to set this field in builder. Giving public access for id might not be good idea (usually.

Can I do this with pojobuilder? Any plan for this? Maybe not with spring which might be to heavy for this but some other library or pure Java...

@mkarneim
Copy link
Owner

PojoBuilder can access protected fields by default, if it's generated into the pojo's package.

If you want to access private fields, you could write some factory method to do it, and annotate it instead of the pojo.

@damianszczepanik
Copy link
Author

Factory sounds good for this case. However the best of this tool is that I don't need to write any class/factory/method and adding new builder = add annotation.

I'm not sure if the factory is good solution for class hierarchy. Two, having factory for class or two is acceptable but not for more than 20-40 classes. Three I'm worry about maintaining those factories if new private field will be added to abstract Animal because then Dog, Cat, Bird... have to be updated as well.

Those builders are perfect and very comfortable as long as they support all fields from whole class hierarchy. Out of the box...

@mkarneim
Copy link
Owner

This is what I do when I want to generate a builder for a JPA-Bean that has a private ID field:

@Entity
@Table(name = "Article")
@SequenceGenerator(name = "seq-art", sequenceName = "SEQ_ART")
public class Article {
  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq-art")
  @Column(name = "ARTICEL_ID", nullable = false)
  private Long id;

  @Column(name = "NAME", nullable = false)
  private String name;

  @GeneratePojoBuilder  // <-- annotate the constructor!
  protected Article(Long id) {
    this.id = id;  // <-- initialization of private fields is done by the constructor
  }

  public Article() {
  }

  public Long getId() {
    return id;
  }
  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

@Adrodoc
Copy link
Contributor

Adrodoc commented Dec 29, 2016

I really like the idea of creating a builder class hierarchy, but it does not allow for exclusion of superclass fields wich is currently possible and might be important for #112. If we ignore that we would need to overwrite all with methods of the builder super class to return the correct self type (this is probably better than using a self generic like in assertj). If this is implemented it might be necessary to always generate builders for all superclasses of each pojoclass to make sure that all common ancestor builders are reused.

@drekbour
Copy link
Contributor

#132 includes Builder for an abstract pojo is itself abstract, probably closes this issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants