# Annotations at a Glance

_Every annotation that generates an optic: what it goes on, and what it writes._

Every optic in this chapter is generated by an annotation. You write a record or sealed type, add the annotation, and the processor produces a typed companion class at compile time. There are no runtime reflection costs and no boilerplate to maintain.

This page is the lookup table. Each row links to the page that explores the annotation in depth.

---

## 1. Generate optics for your records

Apply these to your own records and sealed types. The generated class is placed in the same package by default; pass `targetPackage = "..."` to override. Every type it names has to be visible from that package, so keep a nested type it uses package-private rather than `private` ([Compiler Messages](compiler_errors.md#companion-cannot-be-reached)).

| Annotation | Apply to | Generates | When to reach for it |
|---|---|---|---|
| [`@GenerateLenses`](lenses.md) | `record` | `XLenses` (one lens per field, plus `withFoo` helpers) | Default starting point for any record you want to update functionally |
| [`@GenerateFocus`](focus_dsl.md) | `record` | `XFocus` (path-based DSL builder) | Pair with `@GenerateLenses` when you want fluent, IDE-friendly navigation |
| [`@GenerateFolds`](folds.md) | `record` | `XFolds` (read-only optics) | Querying without modification (CQRS-friendly) |
| [`@GenerateGetters`](getters.md) | `record` | `XGetters` (asymmetric read-only) | When you specifically want a `Getter` rather than a full `Lens` |
| [`@GenerateSetters`](setters.md) | `record` | `XSetters` (asymmetric write-only) | When you specifically want a `Setter` rather than a full `Lens` |
| [`@GenerateTraversals`](traversals.md) | `record` containing `List<T>` etc. | `XTraversals` (one traversal per traversable field) | Bulk operations on a collection embedded in a record |
| [`@GeneratePrisms`](prisms.md) | `sealed interface` **or** `enum` | `XPrisms` (one prism per variant) | Sum types, including both sealed hierarchies and enum constants |
| [`@GenerateIsos`](iso.md) | **static, no-argument method** returning `Iso<A, B>`, naming no type variable | companion class with the iso as a static field | Lossless conversions between equivalent representations |

~~~admonish tip title="Annotations stack"
You almost always want at least `@GenerateLenses` and `@GenerateFocus` together. Add `@GenerateTraversals` if the record contains a collection field and `@GenerateFolds` if you also need read-only queries. The chapter's `Order` stacks three, and its Focus path chains through its `Customer` by a navigator:

``` java
@GenerateLenses
@GenerateFocus
@GenerateTraversals
public record Order(
    UUID id,
    Customer customer,
    List<LineItem> lines,
    Instant placedAt,
    Currency currency,
    OrderStatus status) {}
```
~~~

---

## 2. Generate optics for types you don't own

External types like `LocalDate`, Jackson's `JsonNode`, JOOQ records, and Protobuf messages can't be annotated directly. Two gateways bring them into the optics world.

| Annotation | Apply to | Use when |
|---|---|---|
| [`@ImportOptics`](importing_optics.md) | `package-info.java` or a type | The external type is a record (lenses), sealed interface (prisms), or enum (prisms) and the processor can analyse it directly |
| [`OpticsSpec<S>`](optics_spec_interfaces.md) | extend in your own interface | The external type defies auto-detection (no sealed hierarchy, no copy mechanism, predicate-based type checks). You declare what you want; hint annotations say how to build it. |

`OpticsSpec` interfaces use the spec-method hints below to tell the processor how to generate each optic.

---

## 3. Generate record mappings (domain ↔ DTO)

| Annotation | Apply to | Generates | When to reach for it |
|---|---|---|---|
| [`@GenerateMapping`](../mapping/ch_intro.md) | interface extending `MappingSpec<Domain, Wire>` | `XMappingImpl` with a total `build` and an accumulating, located `parse` | Bidirectional boundary mapping with validation: every bad field reported at once |
| [`@GenerateMapping`](../mapping/beans_patch.md#sparse-patch-write-back-updatespec) | interface extending `UpdateSpec<Domain, Wire>` (bean wire, or a protobuf-java message) | `XMappingImpl` with only `updateFrom(Wire) : Edits.Accumulated<Domain>`, or `updateFrom(Wire, FieldMask)` over a message | Sparse PATCH write-back: fold the present (non-null) request fields into an update, leave the absent ones; over a message, apply every field its `FieldMask` names, set or not, and leave the rest |
| [`@MapField(to = "...")`](../mapping/ch_intro.md) | abstract marker method on the spec, named after the domain component, or that component's `default` leaf | a rename in both directions | Domain and wire components with different names |
| [`@OptionalBridge`](../mapping/absence.md#optional-bridge) | abstract marker method on the spec, or that component's `default` leaf | a domain `Optional<T>` mapped to a nullable wire component `T` | A record wire that encodes absence as `null`, the way a JSON binder writes it |
| [`@MapKey("component")`](../mapping/structure.md#converting-map-keys) | `default` leaf on the spec, freely named, returning `ValidatedPrism<WireKey, DomainKey>` | the named `Map` component's **keys** converted, alone or beside its value leaf | Two sides that key a map differently (`Map<String, ...>` on the wire, `Map<Locale, ...>` in the domain) |
| [`@Flatten`](../mapping/structure.md#flattening-a-nested-component-onto-a-flat-wire) | abstract marker method on the spec, named after a nested domain record component | that record's components spread across the wire's flat components by name, both directions | A wire fixed flat (`street`, `city`, `postcode`) where the domain nests an `Address` |
| [`@GenerateAssembly`](../monads/validated_assembly.md) | `record` | `XAssembly`, a staged builder over `Validated<NonEmptyList<FieldError>, R>` with one method per component | Building a record from independently validated parts, collecting every error, with no arity ceiling |
| [`@GenerateMerge`](../mapping/merge_envelopes.md) | interface whose abstract method names target and sources | a forward-only assembly of one target record from several sources | Merging several records into one; no inverse is generated, because the multi-source case has none |
| [`@GenerateErrorEnvelope`](../mapping/merge_envelopes.md) | `sealed interface` whose variants each carry one `ErrorEnvelope<C>` | per-variant factories, a fluent context builder, and a context wither | Giving a sealed error hierarchy a typed context without repeating it on every variant |

Leaves, nesting, `List`/`Set`/array/`Optional`/`Map` lifting, sealed dispatch, the `asIso()`/`asLens()` tiers, and the sparse `UpdateSpec` tier are covered in [Record Mapping](../mapping/ch_intro.md).

---

## 4. Spec-method hints (inside `OpticsSpec` interfaces)

Apply these to abstract methods inside an interface that extends `OpticsSpec<S>`.

### Prism hints, for sum-type-like external types

| Annotation | Generates a prism via |
|---|---|
| `@InstanceOf(SubType.class)` | Java `instanceof` pattern matching (e.g. Jackson's `ObjectNode`, `ArrayNode`) |
| `@MatchWhen` | Predicate method (`isFoo()`) plus a getter (`asFoo()`), for type-checking APIs that don't use sealed types |

A parameterised `@InstanceOf` target may only promise the type arguments the source type pins down: the class constant is raw, and the test runs after erasure. See [Parameterised Targets](optics_spec_interfaces.md#parameterised-targets).

### Lens hints, copy strategies for legacy Java

External record-like types rarely have a single copy mechanism. The processor uses these hints to know how to rebuild the object after a `set` or `modify`.

| Annotation | Generates a lens that copies via |
|---|---|
| [`@Wither`](copy_strategies.md) | A `withFoo(value)` method on the source |
| [`@ViaBuilder`](copy_strategies.md) | The builder pattern (`source.toBuilder().foo(value).build()`) |
| [`@ViaConstructor`](copy_strategies.md) | The all-args constructor |
| [`@ViaCopyAndSet`](copy_strategies.md) | A copy constructor followed by a setter call (legacy bean style) |

### Traversal hints

| Annotation | Generates |
|---|---|
| `@ThroughField` | A traversal composing a lens-to-field with the auto-detected container traversal |
| `@TraverseWith` | A traversal using an explicitly named `Traverse` instance |

---

## 5. Kind-field configuration (on a record component)

This one is not a spec-method hint: it targets a **record component**, so it goes on the field of your own record, not on a method of an `OpticsSpec` interface.

| Annotation | Apply to | Effect |
|---|---|---|
| [`@TraverseField`](kind_field_support.md) | record component of type `Kind<F, A>` | Names the `Traverse<F>` instance the Focus DSL generator should walk the field with, and the cardinality that decides the path type it generates |

`KindSemantics` is an enum, not a second annotation: it is the value of `@TraverseField`'s own `semantics` element. `traverse` is required and takes a fully qualified expression yielding a `Traverse<F>`, such as a singleton field or a factory call:

```java
@TraverseField(
    traverse = "com.example.TreeTraverse.INSTANCE",
    semantics = KindSemantics.ZERO_OR_ONE)
```

`semantics` defaults to `ZERO_OR_MORE`, which generates a `TraversalPath`; `EXACTLY_ONE` and `ZERO_OR_ONE` both generate an `AffinePath`.

---

## 6. Build setup

The HKJ Gradle and Maven plugins wire the annotation processor in for you. Apply the plugin and every annotation on this page is available immediately. See [Build Plugins: One-Line HKJ Setup](../tooling/gradle_plugin.md) for the plugin DSL, or [Manual Setup](../tooling/manual_setup.md) if you need to configure dependencies by hand.

For compile-time path-type checking, see [Compile-Time Checks](../tooling/compile_checks.md).

---

## 7. Quick decision guide

| You have... | You want to... | Reach for |
|---|---|---|
| A record | Get/set fields functionally | `@GenerateLenses` |
| A record | Navigate deeply with a fluent DSL | `@GenerateLenses` + `@GenerateFocus` |
| A record with a `List` field | Update every element | `@GenerateTraversals` |
| A record | Query without modifying | `@GenerateFolds` |
| A `sealed interface` or `enum` | Operate on one variant | `@GeneratePrisms` |
| Two equivalent types | Convert losslessly | `@GenerateIsos` on a method |
| An external record (in a library) | Lenses for its components | `@ImportOptics` |
| An external sealed/enum (in a library) | Prisms for its variants | `@ImportOptics` |
| `JsonNode`, JOOQ records, anything tricky | Custom optics with copy strategy | `@ImportOptics` on an `OpticsSpec` interface + spec-method hints |
| A domain record and a wire DTO | Map both ways, with validation | `@GenerateMapping` on a `MappingSpec` interface |
| A domain record and a PATCH request bean | Fold the present fields into an update, leave the absent ones | `@GenerateMapping` on an `UpdateSpec` interface |

---

~~~admonish tip title="See Also"
- [Quickstart](quickstart.md): the same annotations doing real work, in three examples
- [Optics for External Types](importing_optics.md): the `@ImportOptics` route in full
- [Taming JSON with Jackson](optics_spec_interfaces.md): the spec-interface route, and when to prefer it
- [Record Mapping](../mapping/ch_intro.md): the mapping and assembly generators in section 3
- [Lens & Prism Journey](../tutorials/optics/lens_prism_journey.md): 33 exercises, hands-on
~~~

---

**Previous:** [Production Readiness](production_readiness.md)
**Next:** [Decision Trees](decision_trees.md)
