Vampire
08/23/2026, 8:27 AM.@ to work-around.
Is there also something similar for calling a private method?
If not (or anyway) is there some way to doing something ad-hoc dynamically like makeDynamic in stc extension but in the actual code? Like dynamic { foo.privateMethod() as String } besides moving it to a helper method and marking that with SKIP I mean.Vampire
08/26/2026, 11:33 PMpaulk_asert
08/27/2026, 12:15 AMInvestigated both against the actual source and verified everything empirically on 6.0.0-beta-2. Here's what I found.
## Q1 — is there aequivalent for private methods?.@
Yes:— the method pointer..&
```groovy
@CompileStatic
class T {
static void main(args) {
println (Q.&hidden)(3) // private static → 6
def q = new Q()
println (q.&inst)(3) // private instance → 103
def mp = Q.&hidden // storing it works too
}
}```
Same mechanism as `.@`: it sidesteps the static path.makes the compiler emit a direct field access instead of property resolution;.@produces a.&that dispatches through the metaclass at runtime, where Groovy's historic leniency about private access applies. Verified for static and instance private members, and with the declaring class written in both Groovy and Java.MethodClosure
is not an alternative — under STC a method reference resolves to a real target and visibility is enforced, so::fails to compile. Only the dynamicQ::hiddenform gets through..&
Caveats: result type is, you're back on dynamic dispatch for that call, and it relies onObjectsucceeding — it will break if the declaring class lives in a JPMS module that doesn't open the package.setAccessible
The explicit spelling, if you'd rather not rely on a subtlety:(orInvokerHelper.invokeMethod(foo, 'privateMethod', [a, b] as Object[])). Both compile underinvokeStaticMethodand work.@CompileStatic
## Q1b — an ad-hocblock?dynamic { }
No built-in, and a library method can't provide one:and@CompileDynamicare@TypeChecked— no expression or local-variable scope — and a closure body written inside a@Target([TYPE, METHOD, CONSTRUCTOR])method is compiled statically along with it, so@CompileStaticcan never work as a helper.dynamic { foo.privateMethod() }
The closest inline form that avoids a named helper method is an anonymous inner class, since that gives you a method to annotate:
```groovy
def r = new Object() {
@CompileDynamic def call() { foo.privateMethod() }
}.call()```
Verified working. But it's ugly enough that theprivate helper is the better answer in practice.@CompileDynamic
## Q2 — will the fix bunch the reports?
Yes, and for a structural reason worth spelling out.is emitted byAccess to P#M is forbiddenduringStaticTypesCallSiteWriter, not during type checking.CLASS_GENERATIONruns at the end of each phase, so one STC error anywhere in the whole compilation means codegen never runs and you see none of the access errors. Demonstrated:failIfErrors()
```// two files, each with one bad private access AND one bogus method call
E1.groovy: 5: [Static type checking] - Cannot find matching method java.lang.Object#noSuchMethodHere()
E2.groovy: 5: [Static type checking] - Cannot find matching method java.lang.Object#alsoMissing()
2 errors ← the two P.M / P.N access errors are invisible```
So today it's a guaranteed two-round grind: round 1 gives STC errors, and only once those are entirely clean does round 2 reveal the access errors. Moving the check into STC — which is what GROOVY-12290 implies — puts them in the same batch. That's a decent secondary argument for the fix.
But that's probably not what's biting them. The cap is, default 10:CompilerConfiguration.tolerance
Copy code14 bad accesses in one file → "10 errors"
And the no-arghardcodesCompilerConfiguration()— it does not readtolerance = 10(only thegroovy.errors.tolerance-taking constructor does), soPropertieson groovyc or Gradle is silently ignored. Confirmed. The working fix is a compiler configuration script, which they're already using anyway:-Dgroovy.errors.tolerance=50
```groovy
configuration.tolerance = 100```
→instead of14 errors.10
Worth telling them explicitly: codegen errors do span files — I get 5 errors across 2 files in a single run. So "findings in one file per run" is the tolerance cap filling up on the first file (or an IDE/Gradle stopping early), not the compiler declining to continue.
paulk_asert
08/27/2026, 12:37 AMVampire
08/27/2026, 2:32 AMand a closure body written inside aHow doesmethod is compiled statically along with it, so@CompileStaticcan never work as a helper.dynamic { foo.privateMethod() }
makeDynamic in an STC extension work then?
Or does that then makes the complete method dynamic?
That would be a quite broad hit, and as far as I remember the docs did not suggest that.
That's a quite dangerous thing to do if you actually want type-checked code.
If that is the case, there should imho be a rather prominent warning in the docs where makeDynamic is described.Vampire
08/27/2026, 9:02 AMcaModelBean.&init() instead of caModelBean.init() to call private method workes like a charm, thanks.