You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by "Mattias Reichel (Jira)" <ji...@apache.org> on 2026/09/24 10:29:00 UTC

[jira] [Created] (GROOVY-12426) @Delegate generates no methods returning a nested class of a joint-compiled Java source, and breaks other implementors of the interface

Mattias Reichel created GROOVY-12426:
----------------------------------------

             Summary: @Delegate generates no methods returning a nested class of a joint-compiled Java source, and breaks other implementors of the interface
                 Key: GROOVY-12426
                 URL: https://issues.apache.org/jira/browse/GROOVY-12426
             Project: Groovy
          Issue Type: Bug
          Components: Compiler
    Affects Versions: 6.0.0
            Reporter: Mattias Reichel


In a joint compilation, if a Groovy interface declares methods whose return type is a static nested class of a Java source in the same compilation, {{@Delegate}} of that interface generates no delegate methods for them. The class then fails the abstract-method check.

After {{@Delegate}} has processed the interface, other classes in the same compilation that implement the interface directly also fail the abstract-method check, even though they implement the method.

This compiles on Groovy 5.1.3 and fails on 6.0.0. {{@CompileStatic}} is not needed. A top-level Java class, or a nested class declared in Groovy, works.

h3. Steps to reproduce

{code:title=src/Payload.java}
public class Payload {
    public static abstract class Result {
        public abstract String text();
    }
}
{code}

{code:title=src/Renderer.groovy}
interface Renderer {
    Payload.Result render(Map arguments)
}
{code}

{code:title=src/DelegatingRenderer.groovy}
class DelegatingRenderer {
    @Delegate final Renderer renderer
    DelegatingRenderer(Renderer renderer) { this.renderer = renderer }
}
{code}

{noformat}
mkdir out
java -cp groovy-6.0.0.jar org.codehaus.groovy.tools.FileSystemCompiler -d out -j src/Payload.java src/Renderer.groovy src/DelegatingRenderer.groovy
{noformat}

h4. Expected

Compilation succeeds, as on Groovy 5.1.3.

h4. Actual

{noformat}
src/DelegatingRenderer.groovy: 1: Can't have an abstract method in a non-abstract class. The class 'DelegatingRenderer' must be declared abstract or the method 'Payload$Result render(java.util.Map)' must be implemented.
 @ line 1, column 1.
   class DelegatingRenderer {
   ^
1 error
{noformat}

h3. Effect on other implementors of the interface

When the {{@Delegate}} class declares the affected method itself, it compiles, but a class that implements the interface directly then fails:

{code:title=src/ForwardingRenderer.groovy}
class ForwardingRenderer {
    @Delegate final Renderer renderer
    ForwardingRenderer(Renderer renderer) { this.renderer = renderer }
    Payload.Result render(Map arguments) { renderer.render(arguments) }
}
{code}

{code:title=src/DirectRenderer.groovy}
class DirectRenderer implements Renderer {
    Payload.Result render(Map arguments) { null }
}
{code}

{noformat}
mkdir out
java -cp groovy-6.0.0.jar org.codehaus.groovy.tools.FileSystemCompiler -d out -j src/Payload.java src/Renderer.groovy src/ForwardingRenderer.groovy src/DirectRenderer.groovy
{noformat}

{noformat}
src/DirectRenderer.groovy: 1: Can't have an abstract method in a non-abstract class. The class 'DirectRenderer' must be declared abstract or the method 'Payload$Result render(java.util.Map)' must be implemented.
{noformat}

{{DirectRenderer}} compiles on its own, and both files compile together on 5.1.3.

h3. Notes

Found while moving Apache Grails to Groovy 6: {{grails-views-gson}} has {{@Delegate GrailsJsonViewHelper}} in {{TemplateRenderer}}, and the interface's {{render(...)}} methods return {{JsonOutput.JsonWritable}}, a nested class of a Java source in the same module. That failed first in {{TemplateRenderer}}, then in {{DefaultGrailsJsonViewHelper}}, which implements the interface directly. The workaround is to write all forwarding methods explicitly instead of using {{@Delegate}}.




--
This message was sent by Atlassian Jira
(v8.20.10#820010)