-
Notifications
You must be signed in to change notification settings - Fork 41.4k
Description
Summary
When implementing multiple interfaces that define default methods with generic type parameters using the same name (e.g., C
), Spring's mapping logic fails to correctly resolve the type used in a @RequestBody
annotated method. Changing the name of the type parameter in one interface to a different letter (e.g., from C
to A
) solves the issue.
This seems to be related to how Spring resolves generic type information in the presence of inheritance with conflicting type parameter names.
Example Setup
public interface BatchCreateController<E, DTO, C, D> {
@PostMapping("/batch")
default ResponseEntity<DTO> batchCreate(@RequestBody List<C> dto) {
// ...
}
}
public abstract class CurrentUserUnmodifiableController<DTO, E, C, D, S>
implements CurrentUserCreateController<E, DTO, C, D> {
// ...
}
public class CartController extends CurrentUserUnmodifiableController<CartResponse, Cart, EmptyCreateCart, Detail, Long>
implements BatchCreateController<Cart, CartResponse, AddCartEntryRequest, Detail> {
// ...
}
In this setup, both interfaces use C as a generic name but refer to different concrete types (EmptyCreateCart vs AddCartEntryRequest).
Here are the DTOs:
@Data
@NoArgsConstructor
@Builder
public class EmptyCreateCart implements Serializable{
}
@With
@Builder
public record AddCartEntryRequest(
@NotBlank(message = "{product.barcode.notblank}") @Size(min = 12, max = 13, message = "{product.barcode.size}") @Pattern(regexp = "^\\d{12,13}$", message = "{product.barcode.pattern}") String barcode,
@NotNull(message = "{ticket.quantity.notnull}") @Positive(message = "{ticket.quantity.positive}") Integer quantity) implements Serializable, SubKeyIdentifiable<String> {
@JsonIgnore
@Override
public String getSubId() {
return barcode();
}
}
Problem
When starting the application, the endpoint mapped at /v1/me/carts/batch was recognized, but the C
parameter type in List<C>
was of type passed to CurrentUserUnmodifiableController
and not BatchCreateController
Workaround / Solution
Simply renaming the generic type parameter from C
to another name like A
in BatchCreateController resolved the issue.
public interface BatchCreateController<E, DTO, A, D> {
@PostMapping("/batch")
default ResponseEntity<DTO> batchCreate(@RequestBody List<A> dto) {
// Now it works
}
}
Expected Behavior
Spring should either:
-
Handle this case safely even with identical generic parameter names across interfaces.
-
Or provide an error/warning message about ambiguous generic type resolution.
Environment
-
Spring Boot: 3.4.4
-
Java: 21
-
No custom deserializers or controllers involved beyond standard @RestController