2

When the Java file is a interface, such as TestInterface.java:

public interface TestInterface {

    void method(String parameterName);

}

I compile with javac -g TestInterface.java and then disassemble with javap -v TestInterface, the output is as follows:

Classfile /private/tmp/TestInterface.class
  Last modified Mar 17, 2022; size 148 bytes
  MD5 checksum da2f58afc0eaf77badc94c90de385198
  Compiled from "TestInterface.java"
public interface TestInterface
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
Constant pool:
  #1 = Class              #7              // TestInterface
  #2 = Class              #8              // java/lang/Object
  #3 = Utf8               method
  #4 = Utf8               (Ljava/lang/String;)V
  #5 = Utf8               SourceFile
  #6 = Utf8               TestInterface.java
  #7 = Utf8               TestInterface
  #8 = Utf8               java/lang/Object
{
  public abstract void method(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_ABSTRACT
}
SourceFile: "TestInterface.java"

And when the Java file is a class, such as TestClass.java:

public class TestClass {

    public void method(String parameterName) {

    }

}

Compile with javac -g TestClass.java and then disassemble with javap -v TestClass, the output is as follows:

Classfile /private/tmp/TestClass.class
  Last modified Mar 17, 2022; size 389 bytes
  MD5 checksum 8e124ecce6632ad6e1a5bb45888a3168
  Compiled from "TestClass.java"
public class TestClass
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #3.#17         // java/lang/Object."<init>":()V
   #2 = Class              #18            // TestClass
   #3 = Class              #19            // java/lang/Object
   #4 = Utf8               <init>
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               LocalVariableTable
   #9 = Utf8               this
  #10 = Utf8               LTestClass;
  #11 = Utf8               method
  #12 = Utf8               (Ljava/lang/String;)V
  #13 = Utf8               parameterName
  #14 = Utf8               Ljava/lang/String;
  #15 = Utf8               SourceFile
  #16 = Utf8               TestClass.java
  #17 = NameAndType        #4:#5          // "<init>":()V
  #18 = Utf8               TestClass
  #19 = Utf8               java/lang/Object
{
  public TestClass();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LTestClass;

  public void method(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=0, locals=2, args_size=2
         0: return
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  this   LTestClass;
            0       1     1 parameterName   Ljava/lang/String;
}
SourceFile: "TestClass.java"

Why is LocalVariableTable not included in the class file of the interface?

JDK version: 1.8.0_311

Poison
  • 389
  • 2
  • 14
  • 1
    When you want to retain the parameter names for abstract methods (as parameters are the only variables in abstract methods), you have to compile with `-parameters`. That uses a different attribute. – Holger Mar 17 '22 at 20:15
  • @Holger Thanks, I know about this feature, but this option is not the default configuration in IDE and maven environment, and `-g` is the default configuration. See: [maven-compiler-plugin ](https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#debug) – Poison Mar 18 '22 at 01:53
  • 2
    It’s still the only option. As the answers explained on the technical level, it’s impossible to have a `LocalVariableTable` for an `abstract` method. The only way to retain the parameter names for abstract methods is the `MethodParameters` attribute. As [this answer](https://stackoverflow.com/a/44075684/2711488) explains, `-parameters` is intentionally off by default . – Holger Mar 18 '22 at 08:21

2 Answers2

6

Because that's not how the class file structure works.

According to the JVM spec, the LocalVariableTable is part of a Code attribute:

The LocalVariableTable attribute is an optional variable-length attribute in the attributes table of a Code attribute (§4.7.3). It may be used by debuggers to determine the value of a given local variable during the execution of a method.

However, interface methods are abstract (i.e. having the flag ACC_ABSTRACT), so they can't have Code attributes in the first place (§4.7.3).

The Code attribute is a variable-length attribute in the attributes table of a method_info structure (§4.6). A Code attribute contains the Java Virtual Machine instructions and auxiliary information for a method [...]

If the method is either native or abstract, and is not a class or interface initialization method, then its method_info structure must not have a Code attribute in its attributes table.

So it is not possible for javac to generate a LocalVariableTable for you abstract interface method, even if it wanted to, because that would not produce a correct class file.

Another way to think about it is that the parameter of the method doesn't actually "exist" until you concretely implement it. After all, an abstract method is supposed to just be a "requirement" without "implementations". I don't really see why you would expect there to be local variables, since they are implementation details.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Because I found that `LocalVariableTableParameterNameDiscoverer` is used in Spring MVC to parse the parameter names of the Controller method, and I don't know why MyBatis does not use a similar method to parse the parameter names in the Mapper interface method. Finally, I found that the Mapper interface does not contain `LocalVariableTable`. This is the reason why I asked this question :-) – Poison Mar 18 '22 at 01:48
5

The LocalVariableTable describes the scope of the variable - the portion of the bytecode in which the variable is accessible.

In a class, the parameter of a non-abstract method has a scope - it's accessible in the whole of the body of the method. Even when the method is "empty", the bytecode still consists of a single return command.

(There's also the implicit this variable in an instance method, which is also accessible in the whole body of the method).

In an abstract interface method (i.e. not a default or static method), there's no bytecode in which that variable is accessible, because the method has no body, and thus no bytecode. Hence, there's no need for the LocalVariableTable.

An abstract method (whether in an interface or a class) is just a specification which says "implementors need to provide this". There is no implementation, no body etc.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • 3
    *"An interface is just a specification which says "implementors need to provide this". There is no implementation, no body etc."* - In Java 8+ an `interface` can have `default` and `static` methods which do have an implementation / body / bytecodes. But certainly, not in the OP's example. – Stephen C Mar 17 '22 at 16:16
  • 1
    @StephenC And indeed we do see `LocalVariableTable` being generated for `default` methods! :-) – Sweeper Mar 17 '22 at 16:21
  • 1
    @StephenC updated to take that into account. – Andy Turner Mar 17 '22 at 16:28