Interface Lens<C,A>

Type Parameters:
C - the container ("whole") type
A - the attribute ("part") type focused by this lens

public interface Lens<C,A>
A typed "focus" on a part A sitting inside a whole C — the object-oriented shape of the well-known functional lens. A Lens<C, A> carries two operations: a get that extracts the inner A from a C, and a set that produces an updated C (or a fresh one) with a new inner A.

Where this useful is when generic library code needs to operate on the A inside caller-supplied Cs without forcing the caller to unwrap everything upfront and re-wrap afterward. The caller passes a lens that knows how to peek at and rebuild their domain object; the library stays oblivious to C beyond the lens.

Example

Suppose the caller has labeled vectors and wants to hand them to a generic clustering API that only cares about the geometric vector inside each one:

 record Labeled(String name, RealVector vector) {}

 Lens<Labeled, RealVector> vectorLens = new Lens<>() {
     public RealVector get(Labeled c) {
         return c.vector();
     }
     public Labeled set(Labeled c, RealVector v) {
         // c == null signals "no existing container, build a fresh one from a"
         return new Labeled(c == null ? "" : c.name(), v);
     }
 };

 // Library code can now generically work over List<Labeled> without ever importing Labeled:
 RealVector v        = pointLens.getNonnull(record);                     // peek
 Labeled  updated    = pointLens.set(record, normalized);                // replace
 Labeled  wrapped    = pointLens.wrap(newVector);                        // build fresh
 Labeled  scaledByTwo = pointLens.map(record, p -> p.multiply(2.0d));    // read-modify-write
 

Composition

Lenses compose: compose(Lens) chains a downstream lens so the result focuses through two layers at once. For example, outerLens.compose(innerLens) produces a lens that reaches the inner A2 of an inner A inside an outer C. identity() returns the trivial lens whose container and part are the same type — useful as a default argument when the caller's input is the attribute.

Contract

  • get may return null if the implementation models an absent attribute. Use getNonnull as a convenience when the caller is sure the attribute is present.
  • set accepts a nullable C: set(null, a) is the "construct a new container around a" case, surfaced as the more readable wrap.
  • Implementations are expected to be pure functions of their inputs (no shared mutable state). Whether set returns a fresh container or mutates c is up to the implementation; callers should always use the returned value rather than assume identity with the argument.
  • Method Summary

    Modifier and Type
    Method
    Description
    default <A2> Lens<C,A2>
    compose(Lens<A,A2> downstream)
    Returns a lens that focuses through this lens and then through downstream, so the resulting lens reaches the inner attribute A2 of an inner A inside a C.
    static <T1, T2> List<T2>
    extract(Lens<T1,T2> lens, List<T1> elements)
    Convenience for the common batch-extract pattern: applies getNonnull(Object) to every element of elements and collects the results into a fresh immutable list.
    get(C c)
    Extracts the focused attribute from c.
    default A
    Like get(Object) but throws NullPointerException if the attribute is missing.
    static <T> Lens<T,T>
    The identity lens: container and attribute are the same type and the lens is a no-op pair of get(t) -> t and set(t, t2) -> t2.
    default C
    map(C c, UnaryOperator<A> operator)
    Read-modify-write: extracts the attribute, applies operator, and returns a container with the result.
    set(C c, A a)
    Returns a container equivalent to c but with the focused attribute set to a.
    default C
    wrap(A a)
    Builds a fresh container around a — equivalent to set(null, a) but reads more naturally at the call site when there is no existing container to update.
  • Method Details

    • getNonnull

      @Nonnull default A getNonnull(@Nonnull C c)
      Like get(Object) but throws NullPointerException if the attribute is missing. Use when the caller is certain the attribute is present and wants the non-null type to flow through downstream code.
      Parameters:
      c - the container; must not be null
      Returns:
      the extracted attribute, never null
      Throws:
      NullPointerException - if get(c) returns null
    • get

      @Nullable A get(@Nonnull C c)
      Extracts the focused attribute from c.
      Parameters:
      c - the container; must not be null
      Returns:
      the attribute, or null if the implementation models an absent attribute
    • wrap

      @Nonnull default C wrap(@Nullable A a)
      Builds a fresh container around a — equivalent to set(null, a) but reads more naturally at the call site when there is no existing container to update.
      Parameters:
      a - the attribute to wrap; may be null if the implementation supports containers without the attribute
      Returns:
      a non-null container holding a
    • set

      @Nonnull C set(@Nullable C c, @Nullable A a)
      Returns a container equivalent to c but with the focused attribute set to a. Passing null for c signals "no existing container, build a fresh one around a" — implementations are expected to handle this case, typically with a default for the unfocused parts of the container.
      Parameters:
      c - the existing container to update, or null to construct a fresh one
      a - the new value of the focused attribute; may be null if the implementation supports containers without the attribute
      Returns:
      a non-null container with the attribute set to a
    • map

      @Nonnull default C map(@Nonnull C c, @Nonnull UnaryOperator<A> operator)
      Read-modify-write: extracts the attribute, applies operator, and returns a container with the result. Reference-equality short-circuits: if operator returns the same instance it received, c itself is returned with no call to set.
      Parameters:
      c - the container; must not be null
      operator - transformation applied to the extracted attribute
      Returns:
      a container reflecting the transformed attribute, possibly c itself
    • compose

      default <A2> Lens<C,A2> compose(@Nonnull Lens<A,A2> downstream)
      Returns a lens that focuses through this lens and then through downstream, so the resulting lens reaches the inner attribute A2 of an inner A inside a C. The composed lens's set implements the standard lens-composition law: get the intermediate A, apply downstream.set to embed a2 into it, then write the new A back into c.
      Type Parameters:
      A2 - the deeply focused attribute type
      Parameters:
      downstream - the inner lens that focuses from the intermediate A onto A2
      Returns:
      a composed lens from C to A2
    • identity

      @Nonnull static <T> Lens<T,T> identity()
      The identity lens: container and attribute are the same type and the lens is a no-op pair of get(t) -> t and set(t, t2) -> t2. Handy as a default argument when the caller's input is already the attribute type, or to seed a chain.
      Type Parameters:
      T - shared container/attribute type
      Returns:
      a lens that focuses each element on itself
    • extract

      @Nonnull static <T1, T2> List<T2> extract(@Nonnull Lens<T1,T2> lens, @Nonnull List<T1> elements)
      Convenience for the common batch-extract pattern: applies getNonnull(Object) to every element of elements and collects the results into a fresh immutable list. Equivalent to a streams pipeline but skips the boilerplate and preserves order.
      Type Parameters:
      T1 - the source ("container") element type
      T2 - the extracted ("attribute") type
      Parameters:
      lens - the lens to apply to each element
      elements - the source list
      Returns:
      an immutable list of extracted attributes in source order; never null
      Throws:
      NullPointerException - if any element extraction yields null