2

I have a simple Java class which contains an anonymous inner class:

package stackoverflow;

public class Outer
{
    public static void main(final String[] arguments)
    {
        System.out.println(new Object() {});
    }
}

With mvn compile running javap Outer* gives class stackoverflow.Outer$1 {, whereas with ./gradlew compileJava and plain javac I get final class stackoverflow.Outer$1 {.

Which configuration option causes the added/missing final?

I'm using Java 1.8.0_232 (OpenJDK), Gradle 6.5, Maven 3.6.3 (maven-compiler-plugin:3.6.1)

EDIT: Although this is marked as a duplicate, you won't find the answer in the linked question. In my case the final does not appear if I compile with "errorprone". If I disable "errorprone", I also get final in the resulting .class file using Maven. Errorprone (in my project) compiles using Java 9, whereas the plain project uses Java 8. See https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.9.5 and https://docs.oracle.com/javase/specs/jls/se9/html/jls-15.html#jls-15.9.5.

C-Otto
  • 5,615
  • 3
  • 29
  • 62
  • 2
    You understand those are different compilers? – Thorbjørn Ravn Andersen Jun 17 '20 at 15:15
  • 2
    @ThorbjørnRavnAndersen No, I was assuming all three approaches use the same compiler. Which one differs from `javac`? Maven, I guess? Why? Where is this configured? – C-Otto Jun 17 '20 at 15:28
  • 1
    If I disable errorprone, I also get the `final` with Maven (both with 3.6.1 and 3.8.1 as the plugin version). – C-Otto Jun 17 '20 at 15:45
  • @Zabuzard no, it doesn't even mention the word "compiler". The linked ticket seems to be related to Java source code, whereas this is concerned with Java Byte code. – C-Otto Jun 17 '20 at 15:48
  • 3
    @Zabuzard *always*? I suggest comparing [the Java 8 version](https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.9.5) with [the Java 9 version](https://docs.oracle.com/javase/specs/jls/se9/html/jls-15.html#jls-15.9.5). It seems that “always” has been started not so long ago… – Holger Jun 17 '20 at 15:56
  • 1
    @Zabuzard since only the surrounding code of an anonymous inner class within the same compilation unit can access it directly, there is no real compatibility issue regarding existing bytecode, as anonymous class and outer class can only get compiled together. In fact, the language semantics that changed, e.g. since Java 9, `CharSequence c2 = (CharSequence)new Object() {};` compiles without errors (despite being obviously useless), has more impact than the bit that is only visible to Reflection. – Holger Jun 17 '20 at 16:11
  • 3
    The change between Java 8 and 9 did not merely update the specification, but actually changed behavior as well, at least for some implementations/compilers. I've updated my answer in the linked question accordingly. The changes are supposedly source-compatible, meaning that all previously valid programs should still be valid (not the other way round, though, as @Holger's example shows). Your Java 8 compiler deviates from the assuption in the [ticket 8161009](https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8161009): "anonymous classes are almost never generated with the ACC_FINAL flag set" – Hulk Jun 17 '20 at 17:07
  • @C-Otto Maven typically use javac (can be configured, rarely is). Gradle to my understanding use the Groovy compiler. – Thorbjørn Ravn Andersen Jun 18 '20 at 07:58

0 Answers0