2

In the java language, a final field takes an immediate value at initialisation and cannot be changed anymore. In java bytecode (jasmin), if I make a final field, it ignores the immediate value I assigned it at initialisation and I can change it like any other variable later on.

Ex. java code :

public class App{
    final int CONST = 2;
    App(){
        CONST = 3;
    }
    public static void main(String[] args){
        App app = new App();
    }
}

Output:

App.java:4 error: cannot assign a value to final variable CONST

Ex. jasmin bytecode :

.class App
.super java/lang/Object

.field private final CONST I = 2 ;!!! the immediate value is ignored, 0 assigned

.method public <init>()V
    .limit stack 3
    .limit locals 1
    aload_0
    invokespecial java/lang/Object/<init>()V

    aload_0
    bipush 3
    putfield App/CONST I ;!!! overwritting final field

    return
.end method

.method public static main([Ljava/lang/String;)V
    .limit stack 1
    .limit locals 1

    new App
    invokespecial App/<init>()V

    return
.end method

Output:

Generated: App.class

No error? I also tested printing out the new CONST value and it works just like a normal variable. Why isn't the final field working like it does in java code?

Henri Latreille
  • 263
  • 1
  • 11
  • 6
    The `final` keyword is enforced _by the compiler only_; there is nothing at the _bytecode level_ enforcing Java's `final`! – fge Feb 10 '16 at 15:29
  • @fge Unless there are follow-up questions (in the comments, or via EDITs), there's hardly more to say - so this could be an answer. (Maybe with some links to JLS or JVMS, but not necessarily) – Marco13 Feb 10 '16 at 16:12
  • 1
    That's not entirely true. There are restrictions on the use of final fields in bytecode. There's just less restrictions than in Java. – Antimony Feb 10 '16 at 16:27

1 Answers1

3

The Java language enforces a lot of constraints that are not enforced at the bytecode level. One of them is handling of final fields.

In Java bytecode, the only constraints on final fields are that static final fields cannot be assigned outside the <clinit> method and nonstatic final fields cannot be assigned outside the <init> methods (i.e constructors).

You can assign to a final field 0, 1, 2 or any number of times. If you chain constructors, you can assign to it in one constructor and then overwrite it in a different constructor.

To find out more about how bytecode works, you should read the JVM Specification.

On a side note, the following are completely different things, despite the deceptively similar syntax.

final int CONST = 2;

.field private final CONST I = 2 ;!!! the immediate value is ignored, 0 assigned

In the first example, this is a (nonstatic) initializer. The body of all initializers is effectively just copypasted into each of your constructors. So you get the same effect as if you had written CONST = 2 in the constructor.

By contrast, the Jasmin syntax is creating a ConstantValue attribute. This is normally used to give an initial value for static final fields. It can be specified for any field but is ignored for non-static fields, hence why you see the value being ignored.

Antimony
  • 37,781
  • 10
  • 100
  • 107
  • in java, `CONST` is also considered a constant variable. But a constant variable per instance is a quite odd use case, and jls and javac don't seem to treat it very thoroughly -- see my [discussion](https://groups.google.com/forum/#!topic/java-lang-fans/AyS3UqX4lj4) – ZhongYu Feb 10 '16 at 17:28
  • This is not entirely correct. When you say `final int CONST = 2;` for a non-`static` field, a conforming compiler creates *both*, a `ConstantValue` attribute and an assignment within the constructor. The point here is that the JVM ignores the `ConstantValue` attribute for non-`static` fields, so the assignment is needed, but compilers will use it to implement the compile-time constant semantic, which does not require the variable to be `static`. In other words, when compiling a different class accessing the `CONST` field, the compiler will copy the value from the `ConstantValue` attribute. – Holger Feb 11 '16 at 09:57
  • @Holger. Interesting, I never noticed that. – Antimony Feb 11 '16 at 14:58
  • It’s often overlooked that variables don’t need to be `static` to be compile-time constants. They don’t even have to be fields, even local variables might be compile-time constants and thus, be used as labels in a `switch` statement or in annotations. However, only fields can be referenced from other compilation units and hence may have a `ConstantValue` attribute. – Holger Feb 11 '16 at 15:19