Skip to content

Replace serialization-based type inference with reified varargs type token#1524

Open
mdproctor wants to merge 1 commit into
serverlessworkflow:mainfrom
mdproctor:refactor/reified-varargs-type-token
Open

Replace serialization-based type inference with reified varargs type token#1524
mdproctor wants to merge 1 commit into
serverlessworkflow:mainfrom
mdproctor:refactor/reified-varargs-type-token

Conversation

@mdproctor

Copy link
Copy Markdown
Contributor

Summary

  • Replace SerializableFunction/SerializablePredicate/SerializableConsumer + ReflectionUtils.inferInputType() with the reified varargs type token pattern across ~40 call sites
  • Delete ReflectionUtils, SerializableFunction, SerializablePredicate, SerializableConsumer — no longer needed
  • Drop Serializable from InstanceIdFunction and UniqueIdBiFunction (was only needed for the inference hack)
  • Add test demonstrating nested generic type safety (Map<String, List<Integer>>)

Motivation

The previous approach used JVM serialization internals (writeReplaceSerializedLambdagetInstantiatedMethodType) to recover erased type parameters at runtime. This is:

  • Fragile — depends on undocumented JVM serialization behavior
  • Restrictive — forces all lambdas to implement Serializable
  • Heavyweight — serialization roundtrip + reflection per inference call

The reified varargs type token pattern achieves the same result (recovering the raw Class<T> at runtime) with zero reflection, zero serialization, and works with any lambda or method reference regardless of serializability. Java reifies array creation at the call site even for generic varargs parameters, so T... typeToken with zero arguments creates a new T[0] whose component type is recoverable via getClass().getComponentType().

Test plan

  • All existing tests pass (0 regressions)
  • New test: function_with_nested_generics_preserves_type_safety — verifies Map<String, List<Integer>> flows through with compile-time type safety
  • IntelliJ build: 0 errors

🤖 Generated with Claude Code

…token

The fluent DSL previously used SerializableFunction/Predicate/Consumer interfaces
and ReflectionUtils.inferInputType() to recover generic type information at runtime.
This relied on JVM serialization internals (writeReplace → SerializedLambda) which
are fragile, heavyweight, and force all lambdas to implement Serializable.

Replace with the reified varargs type token pattern: declaring a trailing T... parameter
causes Java to create a reified array at the call site, making the erased-but-concrete
class recoverable via getClass().getComponentType(). This gives the same runtime
Class<T> with zero reflection, zero serialization, zero allocation, and works with
any lambda or method reference regardless of serializability.

Changes:
- Replace ~40 call sites across FuncDSL, Step, CommonFuncOps, FuncEmitSpec,
  FuncEmitEventPropertiesBuilder, and FuncEventFilterSpec
- Delete ReflectionUtils, SerializableFunction, SerializablePredicate,
  SerializableConsumer (no longer needed)
- Drop Serializable from InstanceIdFunction and UniqueIdBiFunction (was only
  needed for the inference hack; ContextFunction and FilterFunction keep it
  as they are stored in serializable CallJava objects)
- Add test demonstrating nested generic type safety (Map<String, List<Integer>>)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mdproctor mdproctor requested a review from fjtirado as a code owner July 8, 2026 19:18
Copilot AI review requested due to automatic review settings July 8, 2026 19:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes the previous serialization/SerializedLambda-based runtime type inference utilities and migrates the fluent function DSL to a “reified varargs type token” approach for recovering the raw input Class<T> at call sites, eliminating the need for Serializable* functional interfaces and ReflectionUtils.

Changes:

  • Deleted ReflectionUtils and the SerializableFunction/SerializablePredicate/SerializableConsumer helper interfaces; removed Serializable from InstanceIdFunction and UniqueIdBiFunction.
  • Updated fluent DSL entrypoints (FuncDSL, Step, CommonFuncOps, and related specs/builders) to accept standard JDK functional interfaces plus an optional varargs type token for input class inference.
  • Added a test intended to demonstrate nested generic lambda type safety.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/UniqueIdBiFunction.java Drops Serializable from the functional interface now that serialization inference is removed.
experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/SerializablePredicate.java Removed obsolete serializable predicate shim.
experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/SerializableFunction.java Removed obsolete serializable function shim.
experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/SerializableConsumer.java Removed obsolete serializable consumer shim.
experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/ReflectionUtils.java Deleted serialization/reflection-based lambda type inference utility.
experimental/types/src/main/java/io/serverlessworkflow/api/reflection/func/InstanceIdFunction.java Drops Serializable from the functional interface now that serialization inference is removed.
experimental/fluent/func/src/test/java/io/serverlessworkflow/fluent/func/FuncDSLTest.java Adds a nested-generics-focused test for the updated DSL typing behavior.
experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/FuncEmitEventPropertiesBuilder.java Accepts Function directly instead of a serializable wrapper.
experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/Step.java Replaces reflection-based inference with varargs type token input-class derivation for chained operations.
experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncEventFilterSpec.java Switches envelope/data filter overloads from serializable predicates to standard Predicate.
experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncEmitSpec.java Adds varargs type-token overload for JSON event data emission.
experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/FuncDSL.java Updates core DSL helpers to use varargs type tokens for input class inference and removes reflection-based return-type inference wiring.
experimental/fluent/func/src/main/java/io/serverlessworkflow/fluent/func/dsl/CommonFuncOps.java Updates shared DSL ops to accept standard Function/Predicate with type-token inference.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +687 to +690
List<TaskItem> items = wf.getDo();
assertEquals(1, items.size());
assertNotNull(items.get(0).getTask().getCallTask(), "CallTask expected");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants