Is it possible to add a value to a collection in Java AutoValue?
Asked Answered
J

2

10

I have a Java AutoValue class with a List attribute. I'd like to allow the builder to append to the List rather than having to pass the entire constructed list.

Example:

import com.google.auto.value.AutoValue;
@AutoValue
public abstract class Deck {
    public abstract List<Card> cards();
    public static Builder builder() {
        return new AutoValue_Card.Builder()
            .cards(new ArrayList<Card>());
    }
    @AutoValue.Builder
    public abstract static class Builder {
        public abstract Builder cards(List<Card> cards);
        /**
         * Append card to cards in the constructed Deck.
         */
        public Builder addCard(Card card) {
            // Is it possible to write this function?
        }
    }
}

What's the best solution for writing the addCard function? Does AutoValue somehow support this already? The intermediate cards property in the constructed class not visible to the Builder so I can't access it directly. I could try to bypass the Builder directly by keeping my own copy of cards in the Builder, is that the only option here?

Ja answered 11/12, 2015 at 22:52 Comment(0)
F
7

In case someone finds it useful - here is the actual code of what I assume Kevin suggested. It was not obvious when I first stumbled upon his reply, so here you go:

import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;

@AutoValue
public abstract class Hand {
  public static Builder builder() {
    return new AutoValue_Hand.Builder();
  }

  public abstract Builder toBuilder();

  public abstract String name();

  public abstract ImmutableList<String> cards();

  @AutoValue.Builder
  public static abstract class Builder {

    public abstract Builder name(String name);

    protected abstract ImmutableList.Builder<String> cardsBuilder();

    public Builder addCard(String card) {
      cardsBuilder().add(card);
      return this;
    }

    public abstract Hand build();
  }
}

Usage:

Hand fullHouseHand = Hand.builder()
    .name("Full House")
    .addCard("King")
    .addCard("King")
    .addCard("King")
    .addCard("10")
    .addCard("10")
    .build();
System.out.print(fullHouseHand);

Output:

Hand{name=Full House, cards=[King, King, King, 10, 10]}
Franconia answered 14/6, 2016 at 21:54 Comment(0)
C
8

AutoValue has a few special features that come into play when you use Guava's immutable collections, since these collections all have their own associated builder types. (And, of course, you certainly want the properties of your value classes to be immutable!)

One feature is you can define an abstract cardsBuilder() method, which will start a new builder of the appropriate collection type and return it to the caller. This is super flexible, but the one sad thing about it is that it "breaks the chain". The caller is now making calls on an ImmutableList.Builder and can't get back to the Deck.Builder to call build.

But you can address that by adding your own addCard method, just as you've shown it above, and just implement it to call cardsBuilder().add(card) and then return this instead of the builder. Problem solved!

I believe all this should be functional in 1.1 but please let us know if you have trouble!

P.S. I apologize -- we have a lot of new material for the AutoValue user guide, including this topic, that has been held up in getting pushed out to the public site due to a few technical difficulties.

Commode answered 11/12, 2015 at 23:29 Comment(1)
Are there plans for having the same functionality not only for com.google.common.collect builders but for any builder ? I would love to use the same functionality for my generated builders!Lowell
F
7

In case someone finds it useful - here is the actual code of what I assume Kevin suggested. It was not obvious when I first stumbled upon his reply, so here you go:

import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;

@AutoValue
public abstract class Hand {
  public static Builder builder() {
    return new AutoValue_Hand.Builder();
  }

  public abstract Builder toBuilder();

  public abstract String name();

  public abstract ImmutableList<String> cards();

  @AutoValue.Builder
  public static abstract class Builder {

    public abstract Builder name(String name);

    protected abstract ImmutableList.Builder<String> cardsBuilder();

    public Builder addCard(String card) {
      cardsBuilder().add(card);
      return this;
    }

    public abstract Hand build();
  }
}

Usage:

Hand fullHouseHand = Hand.builder()
    .name("Full House")
    .addCard("King")
    .addCard("King")
    .addCard("King")
    .addCard("10")
    .addCard("10")
    .build();
System.out.print(fullHouseHand);

Output:

Hand{name=Full House, cards=[King, King, King, 10, 10]}
Franconia answered 14/6, 2016 at 21:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.