# Parceler: Android Parcelables generated from an annotation

> Parceler is a Java annotation processor that writes Android Parcelable boilerplate for you. It is a good fit for plain data classes with mapped field types, and a poor fit for polymorphic object graphs.

**johncarl81/parceler** — :package: Android Parcelables made easy through code generation.

- Repository: https://github.com/johncarl81/parceler
- Website: http://parceler.org
- Stars: 3,528 · Forks: 272
- Language: Java
- License: Apache-2.0
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/johncarl81-parceler

## The Parcelable boilerplate problem Parceler removes

Android's Parcelable interface is the standard way to move Java objects between Contexts, and the README cites a comparison showing Parcelables take on the order of 10x less time than traditional Serialization to both serialize and deserialize. The cost is manual work. An implementation has to mirror writeToParcel() and createFromParcel() so that reads and writes touch the Parcel in the same order, and it has to declare a public static final Parcelable.Creator so the Android infrastructure can call the serialization code. Every field added to the class means editing both methods in step, and an ordering mistake produces data corruption rather than a compile error.

Parceler targets that specific chore. It is a code generation library, and the README describes it as generating the Parcelable boilerplate source code. The intended user is an Android developer with Java beans that already exist as model classes and need to travel through a Bundle, an Intent or a Fragment argument. The project is not a serialization format, not a database layer, and not a replacement for Serializable in contexts where Parcelable is unavailable.

## How the JSR-269 annotation processor fits into the build

Parceler uses the Java JSR-269 Annotation Processor, so there is no separate generation step to run by hand. The README is explicit about the workflow: annotate a Java Bean, compile, and the Parcelable code exists. The processor reads the @Parcel annotation during compilation and emits a generated class that implements the Parcelable contract for that bean.

By default the processor serializes an instance's fields directly. That default is the reason the README warns against private fields: the default field strategy will fall back to reflection for them and incur a performance penalty. The generated class is usable either directly or through the Parcels utility class, which is the API most call sites will use. Parcels.wrap() returns a Parcelable, and Parcels.unwrap() returns the original type. The README also notes that Parceler supports mapped collection types directly, so wrapping an ArrayList or a HashMap of @Parcel classes is a supported operation rather than something you assemble by hand.

The type support is a closed list, not anything the compiler happens to see. The README enumerates the mapped attribute types: the primitives byte, double, float, int, long, char and boolean, plus String, IBinder, Bundle, SparseArray, SparseBooleanArray, ObservableField, the List, Map and Set families, Parcelable, Serializable, arrays of mapped types, and any other class annotated with @Parcel. Generics are checked. The README states that Parcel will error if the generic parameter is not mapped, which turns an unsupported collection into a build failure instead of a runtime surprise.

## Installing Parceler and wrapping a first bean

The repository is a Maven project with parceler-api and parceler modules, and the README points at Maven Central for the artifact. The documentation does not print a dependency block in the pages available here, so the version and exact coordinates should be taken from the Maven Central page linked in the README rather than guessed. The annotation processor runs at compile time, so the API artifact and the processor artifact are both part of the setup.

Start with a bean. The README's own example uses package-private fields, which is the fast path for the default field strategy:

```java
@Parcel
public class Example {
    String name;
    int age;

    public Example() {}

    public Example(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public String getName() { return name; }

    public int getAge() { return age; }
}
```

After compiling, the generated Parcelable can be obtained through the utility class. The README shows wrapping and unwrapping as a pair:

```java
Parcelable wrapped = Parcels.wrap(new Example("Andy", 42));
Example example = Parcels.unwrap(wrapped);
example.getName(); // Andy
example.getAge(); // 42
```

The wrapped value goes into a Bundle like any other Parcelable, and comes back out in onCreate():

```java
Bundle bundle = new Bundle();
bundle.putParcelable("example", Parcels.wrap(example));
```

```java
Example example = Parcels.unwrap(getIntent().getParcelableExtra("example"));
```

What to check after the first build: the generated class exists, the field order in the generated read and write paths matches, and no reflection warning appears for private fields. If the bean has a non-empty constructor or exposes state only through getters and setters, the default field strategy is the wrong configuration and the getter/setter serialization mode described in the README is the one to use.

## Polymorphism: the case where Parceler is the wrong tool

Parceler does not unwrap inheritance hierarchies. The README states this plainly and gives the reason: Parceler opts for performance rather than checking .getClass() for every piece of data. A field declared as a parent type comes back as an instance of the parent type after a round trip, even when the value put in was a subclass.

The README's own example makes the failure concrete. An Example holds a Parent field, and it is constructed with a Child. Before the round trip, example.p instanceof Child is true. After Parcels.unwrap(Parcels.wrap(example)), the README shows it as false. Nothing throws. The data that distinguished Child from Parent is gone, and the bug appears wherever the code later casts or dispatches on the runtime type.

This is a design trade-off, not an oversight, and it is worth being honest about the shape of it. Paying a getClass() check per field on every parcel operation would tax the common case, which is a flat bean with known field types, to serve the less common case. The README's answer is custom serialization, which it references as the way to work with polymorphic fields. Teams whose models are genuinely polymorphic should either write that custom serialization deliberately or pick a different mechanism; adding @Parcel and assuming the hierarchy survives is the mistake to avoid.

## Where Parceler sits next to hand-written Parcelables

The real alternative is writing the Parcelable by hand. That approach has no annotation processor in the build, no generated source to inspect or debug around, and no type list to satisfy. It also has no mapping rules to learn: whatever the developer writes is what runs, including a getClass() check for a polymorphic field, which Parceler deliberately does not do.

The difference in approach is where the correctness burden lives. With a hand-written Parcelable, read and write order is a human invariant maintained across edits, and the compiler will not catch a mismatch. With Parceler, the order is generated from one source of truth, the bean, and the constraint moves to the type system: fields must be mapped types, generic parameters must be mapped, and unsupported shapes fail at compile time. The README notes Parceler is supported by Transfuse, FragmentArgs, Dart, AndroidAnnotations, ActivityStarter and Remoter, which matters if the project already uses one of those for argument and extra injection. None of that changes the polymorphism boundary. A hand-written Parcelable can preserve a subclass; Parceler will not, unless custom serialization is written for it.

## Maintenance, licence and the cost of upgrading

The repository is not archived, and the last push was on 2026-07-09. The README still points at a Travis CI build badge, which is an older continuous integration setup, and no recent releases were retrieved for this page, so the release cadence cannot be described from the pages available here. The project is published under Apache-2.0, with a NOTICE file in the repository root alongside the LICENSE. Apache-2.0 permits commercial and closed-source use and includes a patent grant; it also carries attribution and notice obligations, and the NOTICE file is the artifact that obligation attaches to. That is a description of the licence text, not legal advice, and teams with specific compliance questions should read the LICENSE and NOTICE files directly.

Upgrade cost is dominated by the annotation processor rather than by a runtime dependency. A version bump can change generated code, so the generated Parcelable for at least one representative bean is worth reading after an upgrade, particularly if that bean uses collections or custom serialization. The CHANGELOG.adoc at the repository root is the file to read before bumping, since it records what changed between versions. Because the processor is tied to the Java compiler, a JDK or Android Gradle Plugin upgrade is the other moment to re-verify generation, not just a Parceler version change.

## Conclusion

Adopt Parceler for Android data classes whose fields are all mapped types and whose object graphs are flat; the annotation processor removes writeToParcel, createFromParcel and CREATOR by hand. Do not adopt it for polymorphic models, since the README states that Parceler unwraps inheritance hierarchies as the base class, or for classes with private fields under the default strategy, which the README says incurs a reflection penalty. Before committing, verify the generated class for one real bean, confirm every generic parameter is a mapped type, and check whether a non-empty constructor or getter/setter serialization is required for your model.

## FAQ

### Does Parceler work with private fields?

It compiles, but the README warns that the default field serialization strategy will fall back to reflection for private fields and incur a performance penalty. Use package-private or accessible fields, or switch to getter/setter serialization.

### Does Parceler preserve subclasses in a field?

No. The README states that Parceler does not unwrap inheritance hierarchies, so a polymorphic field is unwrapped as an instance of the base class. Its example shows a Child stored in a Parent field coming back as not an instance of Child.

### Which types can be fields of a Parceler class?

The README lists primitives, String, IBinder, Bundle, SparseArray, SparseBooleanArray, ObservableField, the List, Map and Set families, Parcelable, Serializable, arrays of those, and any other class annotated with @Parcel. Parcel errors if a generic parameter is not a mapped type.

### Do I have to run a generator manually after annotating a class?

No. Parceler uses the Java JSR-269 Annotation Processor, so the README's workflow is to annotate the bean, compile, and the Parcelable code is generated as part of the build.

## Sources

- [Issues](https://github.com/johncarl81/parceler/issues)
- [johncarl81/parceler on GitHub](https://github.com/johncarl81/parceler)
- [License: Apache-2.0](https://github.com/johncarl81/parceler/blob/master/LICENSE)
- [Project website](http://parceler.org)
- [README](https://github.com/johncarl81/parceler/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/johncarl81-parceler
