8

Is this a bug or a deliberate design decision by the folks at Groovy?

final String x = "a"
x = "b"

You run this and it would work, no problem. Shouldn't a runtime exception be thrown instead? Annotating the class with @CompileStatic didn't help either. I was expecting a compilation error when @CompileStatic is used.

Ankush92
  • 401
  • 1
  • 9
  • 20
  • Possible duplicate of [what does final mean in Groovy](https://stackoverflow.com/questions/1652133/what-does-final-mean-in-groovy) – Joe Dec 21 '17 at 13:52
  • What version of groovy are you running? [GROOVY-1628](https://issues.apache.org/jira/browse/GROOVY-1628) looks related and was fixed with 2.5.0-alpha-1. – Marvin Dec 21 '17 at 13:53
  • Running 2.4.13. I guess I'll wait for 2.5.0 then – Ankush92 Dec 21 '17 at 14:07
  • "Shouldn't a runtime exception be thrown instead? " - No. `final` should not result in a runtime error. I think the only time `final` violations are dealt with is compile time and the answer below about class vs script provides more detail. – Jeff Scott Brown Jan 30 '18 at 02:17

1 Answers1

9

if you compile it as a Script, groovyc will just ignore the final keyword, but if you wrap it into a class, groovyc will raise a compilation error.

fin.groovy with content

final String x = "a"
x = "b"

$ groovyc fin.groovy

Decompile it with ByteCodeViewer

import org.codehaus.groovy.reflection.*;
import java.lang.ref.*;
import groovy.lang.*;
import org.codehaus.groovy.runtime.*;
import org.codehaus.groovy.runtime.callsite.*;

public class fin extends Script
{
    private static /* synthetic */ SoftReference $callSiteArray;

    public fin() {
        $getCallSiteArray();
    }

    public fin(final Binding context) {
        $getCallSiteArray();
        super(context);
    }

    public static void main(final String... args) {
        $getCallSiteArray()[0].call((Object)InvokerHelper.class, (Object)fin.class, (Object)args);
    }

    public Object run() {
        $getCallSiteArray();
        String x = "a";
        return x = "b";
    }

    private static /* synthetic */ CallSiteArray $createCallSiteArray() {
        final String[] array = { null };
        $createCallSiteArray_1(array);
        return new CallSiteArray((Class)fin.class, array);
    }

    private static /* synthetic */ CallSite[] $getCallSiteArray() {
        CallSiteArray $createCallSiteArray;
        if (fin.$callSiteArray == null || ($createCallSiteArray = fin.$callSiteArray.get()) == null) {
            $createCallSiteArray = $createCallSiteArray();
            fin.$callSiteArray = new SoftReference($createCallSiteArray);
        }
        return $createCallSiteArray.array;
    }
}

There is no final anymore, and if you compile it with content

class A{
    final String x = "a"
    def a(){
        x = "b"
    }
}

It gives

$ groovyc fin.groovy 
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
fin.groovy: 4: cannot modify final field 'x' outside of constructor.
 @ line 4, column 3.
   x = "b"
   ^

1 error
jihor
  • 2,478
  • 14
  • 28
Yu Jiaao
  • 4,444
  • 5
  • 44
  • 57