codeconsole opened a new pull request, #15717:
URL: https://github.com/apache/grails-core/pull/15717

   ## Problem
   
   A scaffolded service declared with a parameterized base, e.g. 
`@Scaffold(GormMongoService<Book>)`, is given the **raw** type 
`GormService`/`GormMongoService` as its superclass — the `<Book>` type argument 
is dropped. So every inherited generic method (`get():T`, `list():List<T>`, 
`save(T):T`, …) resolves to the `GormEntity` upper bound rather than the 
concrete domain type.
   
   Under `@GrailsCompileStatic`/`@CompileStatic` this forces a cast at every 
call site:
   
   ```groovy
   Book b         = bookService.get(id)      // Cannot assign value of type 
GormEntity to variable of type Book
   List<Book> all = bookService.list(params) // List<GormEntity>
   ```
   
   ## Root cause
   
   In `ScaffoldingServiceInjector.performInjectionOnAnnotatedClass`:
   
   ```groovy
   superClassNode = valueClassNode.getPlainNodeReference()                    
// strips the <Book> generic
   ...
   classNode.setSuperClass(GrailsASTUtils.nonGeneric(superClassNode, 
domainClass))
   ```
   
   `getPlainNodeReference()` removes the generics, so the following 
`nonGeneric(raw, domain)` is a no-op: `replaceGenericsPlaceholders` 
early-returns the plain node because the type is no longer using generics. The 
domain type is known but never applied to the superclass.
   
   ## Fix
   
   Set the superclass to a parameterized `GormService<Domain>`:
   
   ```groovy
   ClassNode parameterizedSuper = superClassNode.getPlainNodeReference()
   parameterizedSuper.setGenericsTypes(
       [new GenericsType(GrailsASTUtils.nonGeneric(domainClass))] as 
GenericsType[])
   classNode.setSuperClass(parameterizedSuper)
   ```
   
   ## Impact
   
   Inherited scaffold-service methods now resolve to the domain type with no 
casts under static compilation:
   
   ```groovy
   Book b         = bookService.get(id)      // ✅ Book
   List<Book> all = bookService.list(params) // ✅ List<Book>
   ```
   
   Generics are compile-time only, so there is no runtime behavior change. 
Verified against a real app's `@Scaffold(GormMongoService<T>)` services — 
`get()`/`list()` compile to the domain type under `@GrailsCompileStatic` with 
zero casts (previously every such call required a cast or `@CompileDynamic`).
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to