Java Method Chaining Optionally based on arguments
Asked Answered
M

3

2

If a 3rd party requests an argument attachments in a method, how may I avoid using an if and break method chaining, knowing that my argument may be null? The method has the following definition.

// import org.jetbrains.annotations.NotNull;
EmailBuilder withAttachments(@NotNull List<Attachment> attachments);

I would prefer NOT using an if condition for .withAttachments, when attachments == null. I know that javascript has method?(), but what is appropriate for java8, or above? In the case where (attachments == null), I don't want to call .withAttachments() at all. But, I don't see syntax comparable to methodA?() like in javascript, or typescript.

return emailBuilder()
  .withSubject(email.getSubject())
  .withReplyTo(replyAddresses)
  .withAttachments(attachments) // This is conditional...based on attachments
  .withHeader("X-showheader", email.getShowHeader());
  .build();

Would I be required to do this?

EmailBuilder eb = emailBuilder()
  .withSubject(email.getSubject())
  .withReplyTo(replyAddresses);
  if(attachments)
    eb = eb.withAttachments(attachments); // This is conditional...based on attachments
  eb = eb.withHeader("X-showheader", email.getHeader())
  .build;
return eb;
Miocene answered 27/4, 2020 at 22:27 Comment(0)
D
2

I'm assuming you can't change the contract of withAttachments to ignore calls with null? You could, upstream wrap attachments in an Optional and then provide an orElse with an empty, but not null, impl of whatever type attachments is, e.g. (assuming attachments is a List):

Optional<...> optionalAttachments = Optional.ofNullable(attachments);

...

.withAttachments(optionalAttachments.orElse(Collections.emptyList())

UPDATE (based on input from comment, hat tip to Andreas)

You could also achieve this with a ternary, e.g.:

.withAttachments(attachments != null ? attachments : Collections.emptyList())
Davina answered 27/4, 2020 at 22:40 Comment(3)
Rather than using Optional, use a ternary operator: .withAttachments(attachments != null ? attachments : Collections.emptyList()), though you might have to hint it using Collections.<Attachment>emptyList()Lonergan
Thank you, @LonerganDavina
Thank you. I appreciate the ternary operator. The original question was missing the fact that the method included a @NotNull annotation, but this still worked.Miocene
L
4

If withAttachments() doesn't allow a null value, then yes, you need if (attachments != null).

But, since builders don't (generally) require a specific order of method calls, you can clean the code up a bit.

EmailBuilder eb = emailBuilder()
        .withSubject(email.getSubject())
        .withReplyTo(replyAddresses)
        .withHeader("X-showheader", email.getHeader());
if (attachments != null)
    eb.withAttachments(attachments);
return eb.build();
Lonergan answered 27/4, 2020 at 22:38 Comment(3)
You're right. Forgot to format my comment. I posted a new comment.Button
When using kotlin, you could also use attachments?.let{ eb.withAttachments(this) }Button
These are both good answers. I chose the answer below, because I am using Java only in this project, and though I appreciate the note that moving the method to the bottom, the answer below does remove the IF, as I hoped.Miocene
D
2

I'm assuming you can't change the contract of withAttachments to ignore calls with null? You could, upstream wrap attachments in an Optional and then provide an orElse with an empty, but not null, impl of whatever type attachments is, e.g. (assuming attachments is a List):

Optional<...> optionalAttachments = Optional.ofNullable(attachments);

...

.withAttachments(optionalAttachments.orElse(Collections.emptyList())

UPDATE (based on input from comment, hat tip to Andreas)

You could also achieve this with a ternary, e.g.:

.withAttachments(attachments != null ? attachments : Collections.emptyList())
Davina answered 27/4, 2020 at 22:40 Comment(3)
Rather than using Optional, use a ternary operator: .withAttachments(attachments != null ? attachments : Collections.emptyList()), though you might have to hint it using Collections.<Attachment>emptyList()Lonergan
Thank you, @LonerganDavina
Thank you. I appreciate the ternary operator. The original question was missing the fact that the method included a @NotNull annotation, but this still worked.Miocene
M
0

Here is the approach you can use if you can edit or extend the builder.

public class ChainBuilder {

  public ChainBuilder ifApplicable(
    Supplier<Boolean> filter,
    Consumer<ChainBuilder> extension) {
    if (filter.get()) {
      extension.accept(this);
    }
    return this;
  }

  public ChainBuilder withAttribute1(String attribute1) {
    //handle store attribute1;
    return this;
  }

  public ChainBuilder withAttribute2(String attribute2) {
    //handle store attribute2;
    return this;
  }
  
  public SomeData build() {
    return new SomeDate(); //with the optional attributes
  }
}

The client code can chain the methods:

SomeData data = new ChainBuilder()
      .withAttribute1("A")
      .ifApplicable(() -> false, builder -> builder.withAttribute2("B"))
      .build();

It is just an illustration. If you have several conditions, it might make sense to inlcude this into the builder class.

Manet answered 18/11, 2022 at 15:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.