網頁

2020/11/22

Java 合成建構式 synthetic constructor

Java合成建構式 sysnthetic constructor簡介。

Synthetic constructor是指原本不存在於原始碼的建構式,而是在編譯時由complier生成的。

參考java.lang.ConstroctorisSynthetic()說明,可找到此方法定義在介面java.lang.MemberisSynthetic():如果成員是透過complier引入返回true,反之返回false。

Returns true if this member was introduced by the compiler; returns false otherwise.


再參考Java 8的程式語言規格書(Java Language Specification)的13.1 The Form of a Binary的第11項說明:由Java編譯器生成的建構式若不存在於原始碼,不論顯性或隱性,則必須標註為synthetic,除非生成的建構式為類別初始方法(e.g. <init>)。

A construct emitted by a Java compiler must be marked as synthetic if it does not correspond to a construct declared explicitly or implicitly in source code, unless the emitted construct is a class initialization method (JVMS §2.9)..


那什麼情況下complier會生成這些原本不在原始碼中的建構式呢?一個典型例子為取得私有的靜態巢狀類別(private static nested class)實例。

例如下面的SyntheticConstructorDemo中有個私有的靜態巢狀類別PrivateNestedClass

私有的巢狀類別如沒有顯性地寫出建構式,則預設的隱性建構式為私有的(private),不過 Java語言允許外部類別(outer class)可直接建構私有的巢狀類別,然而私有的巢狀類別建構式是私有的,所以實際是透過complier生成的synthetic constructor來建構實例。

SyntheticConstructorDemo.java

ackage com.abc.demo;

import java.lang.reflect.Constructor;

public class SyntheticConstructorDemo {

    public static void main(String[] args) {
        PrivateNestedClass privateNestedClass = new PrivateNestedClass(); // 建構私有的巢狀類別

        Constructor<?>[] constructors = PrivateNestedClass.class.getDeclaredConstructors();
        System.out.println(constructors.length); // 2

        Constructor<?> constructor1 = constructors[0];
        System.out.println(constructor1.getName() + ".isSynthetic()=" + constructor1.isSynthetic()); // com.abc.demo.SyntheticConstructorDemo$PrivateNestedClass isSynthetic()=false

        Constructor<?> constructor2 = constructors[1];
        System.out.println(constructor2.getName() + ".isSynthetic()=" + constructor2.isSynthetic()); // com.abc.demo.SyntheticConstructorDemo$PrivateNestedClass isSynthetic()=true

    }
    
    /** 私有的靜態巢狀類別 */
    private static class PrivateNestedClass {
    }

}

SyntheticConstructorDemomain()中建構PrivateNestedClass的實例,並透過反射取得PrivateNestedClass的建構式,可看出除了預設的私有隱性建構式外,還有另一個complier生成的建構式,其isSynthetic()結果為true。執行結果如下。

2
com.abc.demo.SyntheticConstructorDemo$PrivateNestedClass.isSynthetic()=false
com.abc.demo.SyntheticConstructorDemo$PrivateNestedClass.isSynthetic()=true

如果以Java 1.6的javac去編譯SyntheticConstructorDemo.java會編譯出三個class檔為:

  • SyntheticConstructorDemo.class
  • SyntheticConstructorDemo$PrivateNestedClass.class
  • SyntheticConstructorDemo$1.class

再以javap反組譯SyntheticConstructorDemo$PrivateNestedClass.class會印出以下JVM指令集可看到兩個建構式,第二個非私有的com.abc.demo.SyntheticConstructorDemo$PrivateNestedClass(com.abc.demo.SyntheticConstructorDemo$1)即為complier額外生成的synthetic constructor,接受的參數為編譯時另外產生的SyntheticConstructorDemo$1.class。外部類別透過此synthetic constructor來建構PrivateNestedClass

$ javap -c -p -l SyntheticConstructorDemo.PrivateNestedClass
Warning: File ./SyntheticConstructorDemo$PrivateNestedClass.class does not contain class SyntheticConstructorDemo.PrivateNestedClass
Compiled from "SyntheticConstructorDemo.java"
class com.abc.demo.SyntheticConstructorDemo$PrivateNestedClass {
  private com.abc.demo.SyntheticConstructorDemo$PrivateNestedClass();
    Code:
       0: aload_0
       1: invokespecial #2                  // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 21: 0

  com.abc.demo.SyntheticConstructorDemo$PrivateNestedClass(com.abc.demo.SyntheticConstructorDemo$1);
    Code:
       0: aload_0
       1: invokespecial #1                  // Method "<init>":()V
       4: return
    LineNumberTable:
      line 21: 0
}

javap反組譯SyntheticConstructorDemo印出以下,可看到main()方法中的Code 5: invokespecial調用了synthetic constructor com.abc.demo.SyntheticConstructorDemo$PrivateNestedClass(com.abc.demo.SyntheticConstructorDemo$1)

$ javap -c -p -l SyntheticConstructorDemo
Warning: File ./SyntheticConstructorDemo.class does not contain class SyntheticConstructorDemo
Compiled from "SyntheticConstructorDemo.java"
public class com.abc.demo.SyntheticConstructorDemo {
  public com.abc.demo.SyntheticConstructorDemo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 5: 0

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/abc/demo/SyntheticConstructorDemo$PrivateNestedClass
       3: dup
       4: aconst_null
       5: invokespecial #3                  // Method com/abc/demo/SyntheticConstructorDemo$PrivateNestedClass."<init>":(Lcom/abc/demo/SyntheticConstructorDemo$1;)V
       8: astore_1
       9: ldc           #2                  // class com/abc/demo/SyntheticConstructorDemo$PrivateNestedClass
       ...
    LineNumberTable:
      line 8: 0
      line 10: 9
      ...
}

沒有留言:

張貼留言