CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-google-auto-value--auto-value

Generated immutable value classes for Java 8+ using annotation processing

Pending
Overview
Eval results
Files

serialization.mddocs/

Serialization

The @SerializableAutoValue annotation generates serializable implementations for AutoValue classes that contain non-serializable fields like Optional, ImmutableList, or other modern Java types.

Basic Serializable AutoValue

@SerializableAutoValue
@AutoValue
public abstract class Person implements Serializable {
  public abstract String name();
  public abstract Optional<String> email();
  public abstract ImmutableList<String> hobbies();
  
  public static Person create(String name, Optional<String> email, ImmutableList<String> hobbies) {
    return new AutoValue_Person(name, email, hobbies);
  }
}

The generated code automatically handles serialization of:

  • Optional<T> fields
  • ImmutableList<T>, ImmutableSet<T>, ImmutableMap<K,V>
  • Other common non-serializable types

Usage Example

Person person = Person.create(
    "Alice",
    Optional.of("alice@example.com"),
    ImmutableList.of("reading", "coding", "hiking"));

// Serialize to bytes
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(person);
byte[] serialized = baos.toByteArray();

// Deserialize from bytes
ByteArrayInputStream bais = new ByteArrayInputStream(serialized);
ObjectInputStream ois = new ObjectInputStream(bais);
Person deserialized = (Person) ois.readObject();

System.out.println(person.equals(deserialized)); // true

Supported Types

The serialization extension automatically handles these non-serializable types:

@SerializableAutoValue
@AutoValue
public abstract class TypesExample implements Serializable {
  // Optional types
  public abstract Optional<String> optionalString();
  public abstract OptionalInt optionalInt();
  public abstract OptionalLong optionalLong();
  public abstract OptionalDouble optionalDouble();
  
  // Guava Immutable Collections
  public abstract ImmutableList<String> list();
  public abstract ImmutableSet<Integer> set();
  public abstract ImmutableMap<String, Object> map();
  public abstract ImmutableMultimap<String, String> multimap();
  public abstract ImmutableTable<String, String, Object> table();
  
  // Other Guava types
  public abstract ImmutableRangeSet<Integer> rangeSet();
  public abstract ImmutableRangeMap<Integer, String> rangeMap();
  
  public static TypesExample create(
      Optional<String> optionalString,
      OptionalInt optionalInt,
      OptionalLong optionalLong, 
      OptionalDouble optionalDouble,
      ImmutableList<String> list,
      ImmutableSet<Integer> set,
      ImmutableMap<String, Object> map,
      ImmutableMultimap<String, String> multimap,
      ImmutableTable<String, String, Object> table,
      ImmutableRangeSet<Integer> rangeSet,
      ImmutableRangeMap<Integer, String> rangeMap) {
    return new AutoValue_TypesExample(
        optionalString, optionalInt, optionalLong, optionalDouble,
        list, set, map, multimap, table, rangeSet, rangeMap);
  }
}

Nested Serializable AutoValue

SerializableAutoValue works with nested AutoValue objects:

@SerializableAutoValue
@AutoValue
public abstract class Address implements Serializable {
  public abstract String street();
  public abstract String city();
  public abstract Optional<String> zipCode();
  
  public static Address create(String street, String city, Optional<String> zipCode) {
    return new AutoValue_Address(street, city, zipCode);
  }
}

@SerializableAutoValue
@AutoValue  
public abstract class Company implements Serializable {
  public abstract String name();
  public abstract Address headquarters();
  public abstract ImmutableList<Address> offices();
  public abstract ImmutableMap<String, Optional<String>> contacts();
  
  public static Company create(
      String name,
      Address headquarters,
      ImmutableList<Address> offices,
      ImmutableMap<String, Optional<String>> contacts) {
    return new AutoValue_Company(name, headquarters, offices, contacts);
  }
}

Custom Serializer Extensions

Create custom serializers for types not supported by default:

// Custom type that needs serialization support
public class CustomType {
  private final String data;
  
  public CustomType(String data) {
    this.data = data;
  }
  
  public String getData() {
    return data;
  }
}

// Custom serializer extension
public class CustomTypeSerializerExtension implements SerializerExtension {
  @Override
  public Optional<Serializer> getSerializer(
      TypeMirror typeMirror, 
      SerializerFactory factory) {
    if (isCustomType(typeMirror)) {
      return Optional.of(new CustomTypeSerializer());
    }
    return Optional.empty();
  }
  
  private boolean isCustomType(TypeMirror type) {
    return type.toString().equals(CustomType.class.getName());
  }
  
  private static class CustomTypeSerializer implements Serializer {
    @Override
    public TypeMirror proxyFieldType() {
      return getTypeMirror(String.class);
    }
    
    @Override
    public CodeBlock toProxy(CodeBlock expression) {
      return CodeBlock.of("$L.getData()", expression);
    }
    
    @Override
    public CodeBlock fromProxy(CodeBlock expression) {
      return CodeBlock.of("new $T($L)", CustomType.class, expression);
    }
  }
}

Register the extension in META-INF/services/com.google.auto.value.extension.serializable.serializer.interfaces.SerializerExtension.

Serialization with Builders

SerializableAutoValue works seamlessly with builders:

@SerializableAutoValue
@AutoValue
public abstract class Configuration implements Serializable {
  public abstract String host();
  public abstract Optional<Integer> port();
  public abstract ImmutableSet<String> features();
  public abstract ImmutableMap<String, String> properties();
  
  public static Builder builder() {
    return new AutoValue_Configuration.Builder()
        .features(ImmutableSet.of())
        .properties(ImmutableMap.of());
  }
  
  @AutoValue.Builder
  public abstract static class Builder {
    public abstract Builder host(String host);
    public abstract Builder port(int port);
    public abstract Builder features(Iterable<String> features);
    public abstract Builder properties(Map<String, String> properties);
    
    public abstract Builder addFeature(String feature);
    public abstract Builder putProperty(String key, String value);
    
    public abstract Configuration build();
  }
}

Serialization works with builder-created objects:

Configuration config = Configuration.builder()
    .host("localhost")
    .port(8080)
    .addFeature("ssl")
    .addFeature("compression")
    .putProperty("timeout", "5000")
    .build();

// Serialize and deserialize
byte[] serialized = serialize(config);
Configuration deserialized = deserialize(serialized);
System.out.println(config.equals(deserialized)); // true

Generic SerializableAutoValue

Serialization supports generic types:

@SerializableAutoValue
@AutoValue
public abstract class Container<T> implements Serializable {
  public abstract T value();
  public abstract Optional<String> label();
  public abstract ImmutableList<T> alternatives();
  
  public static <T> Container<T> create(
      T value,
      Optional<String> label,
      ImmutableList<T> alternatives) {
    return new AutoValue_Container<>(value, label, alternatives);
  }
}

Usage with serialization:

Container<String> stringContainer = Container.create(
    "main value",
    Optional.of("primary"),
    ImmutableList.of("alt1", "alt2"));

// Serialization preserves generic type information
byte[] serialized = serialize(stringContainer);
Container<String> deserialized = deserialize(serialized);

Error Handling

SerializableAutoValue provides clear errors for unsupported types:

@SerializableAutoValue
@AutoValue
public abstract class UnsupportedExample implements Serializable {
  // This will cause a compilation error
  public abstract NonSerializableCustomType customField(); // ERROR: No serializer found
  
  public static UnsupportedExample create(NonSerializableCustomType customField) {
    return new AutoValue_UnsupportedExample(customField);
  }
}

The error message will indicate which type needs a custom serializer.

Serialization Performance

The generated serialization code is optimized:

  • Uses direct field access (no reflection at runtime)
  • Minimal object creation during serialization
  • Efficient proxy objects for complex types
  • Lazy initialization of serialization metadata

Version Compatibility

Handle serialization version compatibility:

@SerializableAutoValue
@AutoValue
public abstract class VersionedData implements Serializable {
  private static final long serialVersionUID = 1L;
  
  public abstract String name();
  public abstract Optional<String> description();
  public abstract ImmutableList<String> tags();
  
  // Method to handle version compatibility
  private Object readResolve() {
    // Custom logic for handling old versions
    return this;
  }
  
  public static VersionedData create(String name, Optional<String> description, ImmutableList<String> tags) {
    return new AutoValue_VersionedData(name, description, tags);
  }
}

Integration with Other Extensions

SerializableAutoValue can be combined with other extensions:

@SerializableAutoValue
@AutoValue
public abstract class CachedSerializableData implements Serializable {
  public abstract String data();
  public abstract Optional<ImmutableList<Integer>> values();
  
  @Memoized
  public String processedData() {
    return data().toUpperCase().trim();
  }
  
  @Memoized
  @Override
  public abstract String toString();
  
  public static CachedSerializableData create(String data, Optional<ImmutableList<Integer>> values) {
    return new AutoValue_CachedSerializableData(data, values);
  }
}

The memoized fields are properly handled during serialization/deserialization.

Restrictions

  • The AutoValue class must implement Serializable
  • All custom types in properties must either be serializable or have custom serializers
  • Generic type parameters must be serializable at runtime
  • Builder classes are not automatically serializable (only the built objects are)

Install with Tessl CLI

npx tessl i tessl/maven-com-google-auto-value--auto-value

docs

annotation-generation.md

builders.md

extensions.md

index.md

memoization.md

pretty-strings.md

serialization.md

standalone-builders.md

tagged-unions.md

value-classes.md

tile.json