2222import org .jspecify .annotations .Nullable ;
2323
2424/**
25- * Type -safe representation of a property path using getter method references or lambda expressions.
25+ * Interface providing type -safe property path navigation through method references or lambda expressions.
2626 * <p>
2727 * This functional interface extends {@link PropertyPath} to provide compile-time type safety and refactoring support.
2828 * Instead of using {@link PropertyPath#from(String, TypeInformation) string-based property paths} for textual property
2929 * representation that are easy to miss when changing the domain model, {@code TypedPropertyPath} leverages Java's
3030 * declarative method references and lambda expressions to ensure type-safe property access.
3131 * <p>
32- * Typed property paths can be created directly they are accepted used or conveniently using the static factory method
33- * {@link #of(TypedPropertyPath)} with method references :
32+ * Create a typed property path using the static factory method {@link #of(TypedPropertyPath)} with a method reference
33+ * or lambda, for example :
3434 *
3535 * <pre class="code">
36- * PropertyPath .of(Person::getName);
36+ * TypedPropertyPath<Person, String> name = TypedPropertyPath .of(Person::getName);
3737 * </pre>
38- *
39- * Property paths can be composed to navigate nested properties using {@link #then(TypedPropertyPath)}:
38+ *
39+ * The resulting object can be used to obtain the {@link #toDotPath() dot-path} and to interact with the targetting
40+ * property. Typed paths allow for composition to navigate nested object structures using
41+ * {@link #then(TypedPropertyPath)}:
4042 *
4143 * <pre class="code">
42- * PropertyPath .of(Person::getAddress).then(Address::getCity);
44+ * TypedPropertyPath<Person, String> city = TypedPropertyPath .of(Person::getAddress).then(Address::getCity);
4345 * </pre>
4446 * <p>
45- * The interface maintains type information throughout the property path chain: the {@code T} type parameter represents
46- * its owning type (root type for composed paths), while {@code P} represents the property value type at this path
47- * segment.
47+ * The generic type parameters preserve type information across the property path chain: {@code T} represents the owning
48+ * type of the current segment (or the root type for composed paths), while {@code P} represents the property value type
49+ * at this segment. Composition automatically flows type information forward, ensuring that {@code then()} preserves the
50+ * full chain's type safety.
4851 * <p>
49- * Use method references (recommended) or lambdas that access a property getter to implement {@code TypedPropertyPath}.
50- * Usage of constructor references, method calls with parameters, and complex expressions results in
51- * {@link org.springframework.dao.InvalidDataAccessApiUsageException}. In contrast to method references, introspection
52- * of lambda expressions requires bytecode analysis of the declaration site classes and therefore presence of their
53- * class files .
52+ * Implement {@code TypedPropertyPath} using method references (strongly recommended) or lambdas that directly access a
53+ * property getter. Constructor references, method calls with parameters, and complex expressions are not supported and
54+ * result in {@link org.springframework.dao.InvalidDataAccessApiUsageException}. Unlike method references, introspection
55+ * of lambda expressions requires bytecode analysis of the declaration site classes and thus depends on their
56+ * availability at runtime .
5457 *
55- * @param <T> the owning type of the property path segment, root type for composed paths.
56- * @param <P> the property type at this path segment.
58+ * @param <T> the owning type of this path segment; the root type for composed paths.
59+ * @param <P> the property value type at this path segment.
5760 * @author Mark Paluch
5861 * @since 4.1
5962 * @see PropertyPath#of(TypedPropertyPath)
@@ -76,6 +79,21 @@ public interface TypedPropertyPath<T, P> extends PropertyPath, Serializable {
7679 return TypedPropertyPaths .of (propertyPath );
7780 }
7881
82+ /**
83+ * Syntax sugar to create a {@link TypedPropertyPath} from a method reference or lambda for a collection property.
84+ * <p>
85+ * This method returns a resolved {@link TypedPropertyPath} by introspecting the given method reference or lambda.
86+ *
87+ * @param propertyPath the method reference or lambda.
88+ * @param <T> owning type.
89+ * @param <P> property type.
90+ * @return the typed property path.
91+ * @since 4.1
92+ */
93+ static <T , P > TypedPropertyPath <T , P > ofMany (TypedPropertyPath <T , ? extends Iterable <P >> propertyPath ) {
94+ return (TypedPropertyPath ) TypedPropertyPaths .of (propertyPath );
95+ }
96+
7997 /**
8098 * Get the property value for the given object.
8199 *
@@ -128,4 +146,17 @@ default Iterator<PropertyPath> iterator() {
128146 return TypedPropertyPaths .compose (this , of (next ));
129147 }
130148
149+ /**
150+ * Extend the property path by appending the {@code next} path segment and returning a new property path instance.
151+ *
152+ * @param next the next property path segment as method reference or lambda accepting the owner object {@code P} type
153+ * and returning {@code N} as result of accessing a property.
154+ * @param <N> the new property value type.
155+ * @return a new composed {@code TypedPropertyPath}.
156+ */
157+ default <N extends @ Nullable Object > TypedPropertyPath <T , N > thenMany (
158+ TypedPropertyPath <P , ? extends Iterable <N >> next ) {
159+ return (TypedPropertyPath ) TypedPropertyPaths .compose (this , of (next ));
160+ }
161+
131162}
0 commit comments