Source launcher instantiates wrong class on inherited instance main

XMLWordPrintable

    • Type: CSR
    • Resolution: Approved
    • Priority: P3
    • 27
    • Component/s: tools
    • None
    • behavioral
    • minimal
    • Hide
      This change fixes the behavior of the source launcher to match the behavior of the class launcher. It is unlikely that any application relies on the current behavior.

      For the case where a instance main is inherited from an interface or abstract class there is a failure today, which is unlikely to be relied on. For the case when inheriting main from another non-abstract class behavior can change.

      As this only affects the source code launcher in a rather new feature there shouldn't be a large compatibility impact.
      Show
      This change fixes the behavior of the source launcher to match the behavior of the class launcher. It is unlikely that any application relies on the current behavior. For the case where a instance main is inherited from an interface or abstract class there is a failure today, which is unlikely to be relied on. For the case when inheriting main from another non-abstract class behavior can change. As this only affects the source code launcher in a rather new feature there shouldn't be a large compatibility impact.
    • Other
    • JDK

      Summary

      Modify the java source launcher to execute an inherited instance main method from the referenced class, rather than from the declaring type. This matches the current behavior of the java class launcher.

      Problem

      In the source launcher the candidate main method is executed in the context of the declaring class/interface/abstract class, rather than in the context of the referenced class:

      In the following example the launcher should call new A().main(), but rather new B().main() is called.

      Example 1:

      Code

      // A.java
      class A extends B {}
      
      // B.java
      class B {
          void main() {
              System.out.println(getClass().getName());
          }
      }
      

      Commands

      $ java A.java
      B // expected "A" here
      
      $ java B.java
      B
      

      When using the same example with: "javac A.java" and "java A" we get the expected output of "A".

      When A inherits its instance main method, either from an abstract class or from a default method on an interface, the source launcher also attempts to instantiate the declaring type. In both cases this results in an output of error: abstract class: B cannot be instantiated. Here the "javac + class launcher" path handles both cases correctly and prints the expected "A".

      Solution

      Remove the logic for recovering the main class from the main method's declaring class:

      Class<?> mainClass = mainMethod.getDeclaringClass();

      And rather store the referenced class where we found the mainMethod and use this as the context to execute from.

      Specification

      No changes to the specification.

      The specification is clear here, that the initial class (referred to above as referenced class) is the class of which an instance must be created and not the declaring class.

      "If the candidate main method is an instance method then first an instance of the initial class must be created." -- JLS 12.1.4

      The standard java launcher semantics match this statement, but the source launcher semantics do not.

            Assignee:
            Jonathan Lampérth
            Reporter:
            Jonathan Lampérth
            Chen Liang, Christian Stein
            Votes:
            1 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: