7

Final methods in java cannot be overridden by the inherited class.

But why the final methods are invoked using invokevirtual instead of invokespecial.

Hearen
  • 7,420
  • 4
  • 53
  • 63
deshmanth
  • 529
  • 5
  • 12
  • from the above link **When you invoke main() in Subclass as defined above, it must print "Superclass's interesting method**, why not invoke the interesting method on object instance type. – deshmanth Apr 03 '18 at 15:20
  • 3
    Well, for starters, the class could be recompiled without `final` without recompiling the class that calls the method... – Louis Wasserman Apr 03 '18 at 16:19
  • final is a keyword enforced by the compiler; it has no influence on the generated bytecode https://stackoverflow.com/questions/7704024/how-can-one-tell-if-a-local-variable-is-final-from-java-bytecode-related-to –  Apr 04 '18 at 07:22
  • @LouisWasserman That's precisely what IncompatibleClassChangeError is for. Same applies to static modifier. – Julian Durchholz Aug 13 '22 at 04:03
  • @JulianDurchholz - Incorrect. *"Changing a class that is declared `final` to no longer be declared `final` **does not break compatibility** with pre-existing binaries. "* - [JLS 13.4.2](https://docs.oracle.com/javase%2Fspecs%2Fjls%2Fse7%2Fhtml%2F%2F/jls-13.html#jls-13.4.2) – Stephen C Aug 13 '22 at 05:48
  • @StephenC You're right (although 13.4.17 applies), but we're already talking about a hypothetical specification in which a final method could legally be invoked as special. – Julian Durchholz Aug 13 '22 at 05:53
  • yea ... i used the wrong reference. will fix later – Stephen C Aug 13 '22 at 06:08

3 Answers3

4

The semantics of the final modifier are only enforced by the bytecode compiler.

The link to JavaSE7 specification Some other samples about final keyword.

The difference between the invokespecial and the invokevirtual instructions is that invokevirtual invokes a method based on the class of the object. The invokespecial instruction is used to invoke instance initialization methods as well as private methods and methods of a superclass of the current class.

When we invoke a final method, corresponding bytecode instruction is INVOKEVIRTUAL, the same as other non-final method.

Here is a example of invoking method with final keyword.

CounterPoint.java

public class CounterPoint extends Point {

    private static final AtomicInteger counter = new AtomicInteger();

    public CounterPoint(int x, int y) {
        super(x, y);
        counter.incrementAndGet();
    }

    public static String numberCreated() {
        return counter.toString();
    }

}

ByteCode of CounterPoint

// class version 52.0 (52)
// access flags 0x21
public class com/xetrasu/CounterPoint extends java/awt/Point  {

  // compiled from: CounterPoint.java

  // access flags 0x1A
  private final static Ljava/util/concurrent/atomic/AtomicInteger; counter

  // access flags 0x1
  public <init>(II)V
   L0
    LINENUMBER 11 L0
    ALOAD 0
    ILOAD 1
    ILOAD 2
    INVOKESPECIAL java/awt/Point.<init> (II)V
   L1
    LINENUMBER 12 L1
    GETSTATIC com/xetrasu/CounterPoint.counter : Ljava/util/concurrent/atomic/AtomicInteger;
    INVOKEVIRTUAL java/util/concurrent/atomic/AtomicInteger.incrementAndGet ()I
    POP
   L2
    LINENUMBER 13 L2
    RETURN
   L3
    LOCALVARIABLE this Lcom/xetrasu/CounterPoint; L0 L3 0
    LOCALVARIABLE x I L0 L3 1
    LOCALVARIABLE y I L0 L3 2
    MAXSTACK = 3
    MAXLOCALS = 3

  // access flags 0x9
  public static numberCreated()Ljava/lang/String;
   L0
    LINENUMBER 16 L0
    GETSTATIC com/xetrasu/CounterPoint.counter : Ljava/util/concurrent/atomic/AtomicInteger;
    INVOKEVIRTUAL java/util/concurrent/atomic/AtomicInteger.toString ()Ljava/lang/String;
    ARETURN
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x8
  static <clinit>()V
   L0
    LINENUMBER 8 L0
    NEW java/util/concurrent/atomic/AtomicInteger
    DUP
    INVOKESPECIAL java/util/concurrent/atomic/AtomicInteger.<init> ()V
    PUTSTATIC com/xetrasu/CounterPoint.counter : Ljava/util/concurrent/atomic/AtomicInteger;
    RETURN
    MAXSTACK = 2
    MAXLOCALS = 0
}

AtomicInteger.java

public class AtomicInteger extends Number implements java.io.Serializable {
    public String toString() {
        return Integer.toString(get());
    }
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
}
Hearen
  • 7,420
  • 4
  • 53
  • 63
0

Sounds like what you're actually asking is:

Why would we need a vtable lookup for a method that could be linked statically?

And rest assured, in practice this lookup is optimized away during linkage - at least in HotSpot.

The fact final instance methods are treated as virtual rather than special seems like a technicality of the JVMS.

Julian Durchholz
  • 352
  • 3
  • 12
0

It is necessary to use INVOKEVIRTUAL to call a final method in order to implement the binary compatibility rule in JLS 13.4.17.

"Changing a method that is declared final to no longer be declared final does not break compatibility with pre-existing binaries. "

If calls to final method were compiled to INVOKESPECIAL, then those calls would need to be recompiled if the method was changed to not be final. That violates the rule above.

But as the other answers / comments have already noted, the JIT compiler will choose the best calling sequence anyway. Even if you don't declare a method as final, the JIT compiler can figure out when no vtable dispatching is necessary. (And it can even recompile methods when a new class is loaded that invalidates the preconditions.)

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • It's worth keeping in mind that other JVM languages might not impose this constraint. But JVMS already forbids `invokespecial` from crossing the class hierarchy, so you couldn't target final instance methods in the general case anyway. – Julian Durchholz Aug 13 '22 at 06:33