2020import  java .util .Iterator ;
2121
2222import  org .jspecify .annotations .Nullable ;
23- 
2423import  org .springframework .data .util .TypeInformation ;
2524
2625/** 
26+  * Type-safe representation of a property path expressed through method references. 
27+  * <p> 
28+  * This functional interface extends {@link PropertyPath} to provide compile-time type safety when declaring property 
29+  * paths. Instead of using {@link PropertyPath#from(String, TypeInformation) string-based property paths} that represent 
30+  * references to properties textually and that are prone to refactoring issues, {@code TypedPropertyPath} leverages 
31+  * Java's declarative method references and lambda expressions to ensure type-safe property access. 
32+  * <p> 
33+  * Typed property paths can be created directly they are accepted used or conveniently using the static factory method 
34+  * {@link #of(TypedPropertyPath)} with method references: 
35+  *  
36+  * <pre class="code"> 
37+  * PropertyPath.of(Person::getName); 
38+  * </pre> 
39+  *  
40+  * Property paths can be composed to navigate nested properties using {@link #then(TypedPropertyPath)}: 
41+  *  
42+  * <pre class="code"> 
43+  * PropertyPath.of(Person::getAddress).then(Address::getCountry).then(Country::getName); 
44+  * </pre> 
45+  * <p> 
46+  * The interface maintains type information throughout the property path chain: the {@code T} type parameter represents 
47+  * its owning type (root type for composed paths), while {@code P} represents the property value type at this path 
48+  * segment. 
49+  * <p> 
50+  * As a functional interface, {@code TypedPropertyPath} should be implemented as method reference (recommended). 
51+  * Alternatively, the interface can be implemented as lambda extracting a property value from an object of type 
52+  * {@code T}. Implementations must ensure that the method reference or lambda correctly represents a property access 
53+  * through a method invocation or by field access. Arbitrary calls to non-getter methods (i.e. methods accepting 
54+  * parameters or arbitrary method calls on types other than the owning type are not allowed and will fail with 
55+  * {@link org.springframework.dao.InvalidDataAccessApiUsageException}. 
56+  * <p> 
57+  * Note that using lambda expressions requires bytecode analysis of the declaration site classes and therefore presence 
58+  * of their class files. 
59+  *  
60+  * @param <T> the owning type of the property path segment, but typically the root type for composed property paths. 
61+  * @param <P> the property value type at this path segment. 
2762 * @author Mark Paluch 
63+  * @see PropertyPath 
64+  * @see #of(TypedPropertyPath) 
65+  * @see #then(TypedPropertyPath) 
2866 */ 
2967@ FunctionalInterface 
30- public  interface  TypedPropertyPath <T , R > extends  Serializable ,  PropertyPath  {
68+ public  interface  TypedPropertyPath <T , P > extends  PropertyPath ,  Serializable  {
3169
32- 	R  get (T  obj );
70+ 	/** 
71+ 	 * Syntax sugar to create a {@link TypedPropertyPath} from a method reference or lambda. 
72+ 	 * <p> 
73+ 	 * This method returns a resolved {@link TypedPropertyPath} by introspecting the given lambda. 
74+ 	 * 
75+ 	 * @param lambda the method reference or lambda. 
76+ 	 * @param <T> owning type. 
77+ 	 * @param <P> property type. 
78+ 	 * @return the typed property path. 
79+ 	 */ 
80+ 	static  <T , P > TypedPropertyPath <T , P > of (TypedPropertyPath <T , P > lambda ) {
81+ 		return  TypedPropertyPaths .of (lambda );
82+ 	}
83+ 
84+ 	/** 
85+ 	 * Get the property value for the given object. 
86+ 	 * 
87+ 	 * @param obj the object to get the property value from. 
88+ 	 * @return the property value. 
89+ 	 */ 
90+ 	@ Nullable 
91+ 	P  get (T  obj );
3392
3493	@ Override 
3594	default  TypeInformation <?> getOwningType () {
36- 		return  PropertyPathExtractor .getPropertyPathInformation (this ).owner ();
95+ 		return  TypedPropertyPaths .getPropertyPathInformation (this ).owner ();
3796	}
3897
3998	@ Override 
4099	default  String  getSegment () {
41- 		return  PropertyPathExtractor .getPropertyPathInformation (this ).property ().getName ();
100+ 		return  TypedPropertyPaths .getPropertyPathInformation (this ).property ().getName ();
42101	}
43102
44103	@ Override 
45104	default  TypeInformation <?> getTypeInformation () {
46- 		return  PropertyPathExtractor .getPropertyPathInformation (this ).propertyType ();
105+ 		return  TypedPropertyPaths .getPropertyPathInformation (this ).propertyType ();
47106	}
48107
49108	@ Override 
@@ -62,8 +121,14 @@ default Iterator<PropertyPath> iterator() {
62121		return  Collections .singletonList ((PropertyPath ) this ).iterator ();
63122	}
64123
65- 	default  <N > TypedPropertyPath <T , N > then (TypedPropertyPath <R , N > next ) {
66- 		return  new  ComposedPropertyPath <>(this , next );
124+ 	/** 
125+ 	 * Extend the property path by appending the {@code next} path segment and returning a new property path instance.. 
126+ 	 * 
127+ 	 * @param next the next property path segment accepting a property path owned by the {@code P} type. 
128+ 	 * @param <N> the new property value type. 
129+ 	 * @return a new composed {@code TypedPropertyPath}. 
130+ 	 */ 
131+ 	default  <N > TypedPropertyPath <T , N > then (TypedPropertyPath <P , N > next ) {
132+ 		return  TypedPropertyPaths .compose (this , of (next ));
67133	}
68- 
69134}
0 commit comments