Как создать подкласс класса AutoValue?

Я использую расширение AutoValue для создания своих классов Parcelable для Android. В документации конкретно указано, что одно @AutoValue не может расширять другое: https://github.com/google/auto/blob/master/value/userguide/howto.md#inherit

Другой разработчик помогал мне с этим и предложил: «Если у вас есть общие поля, вы можете поместить их в интерфейс, который реализуют обе реализации». Я предполагаю, что это означает, что библиотека предпочитает композицию наследованию.

Признаюсь, я немного растерялся. Если бы кто-то мог предоставить простой конкретный пример простейшего способа "подкласса" класса AutoValue, это было бы признательно. Вот простой класс:

@AutoParcelGson
public abstract class User implements Parcelable {

    public abstract String username();
    public static User create(String username) {
        return builder().username(username).build();
    }

    public static Builder builder() {
        return new AutoParcelGson_User.Builder();
    }

    @AutoParcelGson.Builder
    public interface Builder {
        Builder username(String username);
        User build();
    }
}

Я хотел бы иметь еще один класс @AutoValue под названием Customer с несколькими дополнительными полями. Кроме того, @AutoParcelGson — это расширение @AutoValue, которое я использую, но это то же самое поведение.


person jwBurnside    schedule 30.09.2016    source источник


Ответы (2)


В документации AutoValue говорится: «Это способность намеренно не поддерживается, потому что нет возможности сделать это правильно. См. «Эффективная Java», 2-е издание, пункт 8: «Соблюдайте общий контракт при переопределении равенства».» Хорошая книга, многому научила. В 3-м издании это пункт 10. Вкратце: допустим, вы расширяете свой User класс на BetterUser. BetterUser betterUser.equals(User user) может быть истинным в некоторых случаях, но в этих случаях user.equals(betterUser) не определено. Симметрия нарушена. И это только одна из проблем; чем больше стараешься, тем хуже становится. Кстати, это не относится к классам AutoValue, а является общей проблемой наследования.

Единственное согласованное решение — создать новый класс BetterUser, который не расширяет User, но имеет поле типа User. Таким образом, вы можете определить поведение BetterUser по своему усмотрению, без какого-либо влияния на другие классы. Или, возможно, если User имеет только несколько полей, просто скопируйте и вставьте код.

person Christian H. Kuhn    schedule 28.07.2019

Вот пример рабочего кода. Тест в моем случае печатает {"newString":"foobar","myInt":1,"myString":"asdf"}.

import com.fasterxml.jackson.annotation.JsonProperty;
public abstract class BaseEvent {
  @JsonProperty("myInt")
  public abstract int myInt();
  @JsonProperty("myString")
  public abstract String myString();
  public static abstract class Builder <B extends Builder<B>> {
    @JsonProperty("myInt")
    public abstract B myInt(int myInt);
    @JsonProperty("myString")
    public abstract B myString(String myString);
  }
}



import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.auto.value.AutoValue;
@AutoValue
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonSerialize(as = ConcreteEvent.class)
@JsonDeserialize(builder = ConcreteEvent.Builder.class)
public abstract class ConcreteEvent extends BaseEvent {
  public static Builder builder() {
    return Builder.builder();
  }
  @JsonProperty("newString")
  public abstract String newString();
  @AutoValue.Builder
  @JsonIgnoreProperties(ignoreUnknown = true)
  public static abstract class Builder extends BaseEvent.Builder<Builder> {
    @JsonCreator
    public static Builder builder() {
      return new AutoValue_ConcreteEvent.Builder();
    }
    @JsonProperty("newString")
    public abstract Builder newString(String newString);
    public abstract ConcreteEvent build();
  }
}





import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import org.junit.Assert;
import org.junit.Test;
public class ConcreteEventTest {
  @Test
  public void concreteEventTest() throws IOException  {
    ConcreteEvent e = ConcreteEvent.builder().myInt(1).myString("asdf").newString("foobar").build();
    ObjectMapper mapper = new ObjectMapper();
    String jsonStr = mapper.writeValueAsString(e);
    ConcreteEvent newEvent = mapper.readValue(jsonStr, ConcreteEvent.class);
    System.out.println(jsonStr);
    Assert.assertEquals(newEvent, e);
  }
}
person user1418199    schedule 17.08.2020