Java 重構與原始碼動作

Visual Studio Code 提供了許多重構原始碼的選項,以及在編寫程式碼時用於生成程式碼和修復問題的「原始碼動作」。若要使用這些功能,請在看到燈泡 💡 圖示時點擊它,或者在編輯器視窗中按右鍵並選擇原始碼動作... (Source Action...)

支援的程式碼動作列表

重構

Java 程式重構的目標是在不影響程式行為的前提下,進行系統性的程式碼變更。VS Code 的 Java 語言支援功能提供了許多易於使用的重構選項。

呼叫重構

重構指令可透過編輯器的右鍵選單取得。選擇您想要重構的元素,按右鍵開啟選單,然後選擇重構... (Refactor...)

Invoke Refactoring

接著您將會看到所有可用的重構選項。

指派給變數 (Assign to variable)

將表達式指派給區域變數或欄位。

範例

修改前
Arrays.asList("apple", "lemon", "banana");
修改後
List<String> fruits = Arrays.asList("apple", "lemon", "banana");

它也可以用來將建構函式中未使用的參數指派給新的欄位。

將匿名類別轉換為巢狀類別 (Convert anonymous to nested class)

將匿名內部類別轉換為成員類別。

範例

讓我們將匿名類別 Interface(){...} 轉換為 Clazz 類別的成員。

修改前
public class Clazz {
  public Interface method() {
    final boolean isValid = true;
    return new Interface() {
      public boolean isValid() {
        return isValid;
      }
    };
  }
}
修改後
public class Clazz {
  private final class MyInterface extends Interface {
    private final boolean isValid;

    private MyInterface(boolean isValid) {
      this.isValid = isValid;
    }

    public boolean isValid() {
      return isValid;
    }
  }

  public Interface method() {
    final boolean isValid = true;
    return new MyInterface(isValid);
  }
}

轉換為匿名類別實例化 (Convert to anonymous class creation)

將 Lambda 表達式轉換為匿名類別實例化。

範例

變數 runnable 被指派了一個 Lambda 表達式。讓我們將它轉換為匿名類別實例化。

修改前
public void method() {
  Runnable runnable = () -> {
    // do something
  };
}
修改後
public void method() {
  Runnable runnable = new Runnable() {
    @Override
    public void run() {
      // do something
    }
  };
}

另請參閱:轉換為 Lambda 表達式

轉換為增強型 for 迴圈 (Convert to enhanced for loop)

將簡單的 for 迴圈轉換為 for-each 風格。

範例

修改前
public void order(String[] books) {
  for (int i = 0; i < books.length; i++) {
    // do something
  }
}
修改後
public void order(String[] books) {
  for (String book : books) {
    // do something
  }
}

轉換為 Lambda 表達式 (Convert to lambda expression)

將匿名類別實例化轉換為 Lambda 表達式。

範例

讓我們將匿名類別 Runnable(){...} 轉換為 Lambda 表達式。

修改前
public void method() {
  Runnable runnable = new Runnable(){
    @Override
    public void run() {
      // do something
    }
  };
}
修改後
public void method() {
    Runnable runnable = () -> {
      // do something
    };
  }

另請參閱:轉換為匿名類別實例化

轉換為靜態匯入 (Convert to static import)

將欄位或方法轉換為靜態匯入。

範例

讓我們將 Assert.assertEquals() 的呼叫轉換為靜態匯入。

修改前
import org.junit.Assert;
...
public void test() {
  Assert.assertEquals(expected, actual);
}
修改後
import static org.junit.Assert.assertEquals;
...
public void test() {
  assertEquals(expected, actual);
}

擷取為常數 (Extract to constant)

從所選的表達式建立靜態 final 欄位,並取代欄位參照,隨後重寫程式碼中出現相同表達式的其他位置。

範例

讓我們將 π 的值:3.14 擷取為常數。

修改前
public double getArea(double r) {
  return 3.14 * r * r;
}
修改後
private static final double PI = 3.14;

public double getArea(double r) {
  return PI * r * r;
}

另請參閱:內聯常數

擷取為欄位 (Extract to field)

宣告一個新欄位並使用所選表達式初始化它。原始表達式將被該欄位的使用取代。

範例

讓我們將變數 area 擷取為 Square 類別的欄位。

修改前
class Square {
  public void calculateArea() {
    int height = 1;
    int width = 2;
    int area = height * width;
  }
}
修改後
class Square {
  private int area;

  public void calculateArea() {
    int height = 1;
    int width = 2;
    area = height * width;
  }
}

當選擇變數宣告時,將該變數轉換為欄位。

擷取為方法 (Extract to method)

建立一個包含當前所選語句或表達式的新方法,並將選取範圍取代為對該新方法的參照。此功能對於清理冗長、雜亂或過於複雜的方法非常有用。

範例

讓我們將表達式 height * width 擷取為一個新方法。

修改前
public void method() {
  int height = 1;
  int width = 2;
  int area = height * width;
}
修改後
public void method() {
  int height = 1;
  int width = 2;
  int area = getArea(height, width);
}

private int getArea(int height, int width) {
  return height * width;
}

另請參閱:內聯方法

擷取為區域變數 (Extract to local variable)

建立一個指派給當前所選表達式的新變數,並將選取範圍取代為對該新變數的參照。

範例

讓我們將表達式 platform.equalsIgnoreCase("MAC") 擷取為一個新變數。

修改前
public void method() {
  if (platform.equalsIgnoreCase("MAC")) {
    // do something
  }
}
修改後
public void method() {
  boolean isMac = platform.equalsIgnoreCase("MAC");
  if (isMac) {
    // do something
  }
}

擷取後,您也可以在同一個交易中執行重新命名。

另請參閱:內聯區域變數

內聯常數 (Inline constant)

以常數定義的值取代其參照。

範例

讓我們將常數 PI 取代為其定義值:3.14

修改前
private static final double PI = 3.14;

public double getArea(double r) {
  return PI * r * r;
}
修改後
private static final double PI = 3.14;

public double getArea(double r) {
  return 3.14 * r * r;
}

另請參閱:擷取為常數

內聯區域變數 (Inline local variable)

以其初始化式取代多餘的變數使用。

範例

讓我們將變數 isMac 直接取代為布林表達式。

修改前
public void method() {
  boolean isMac = platform.equalsIgnoreCase("MAC");
  if (isMac) {
    // do something
  }
}
修改後
public void method() {
  if (platform.equalsIgnoreCase("MAC")) {
    // do something
  }
}

另請參閱:擷取為區域變數

內聯方法 (Inline method)

以方法本體取代對該方法的呼叫。

範例

讓我們將方法 getArea(int height, int width) 直接取代為表達式 height * width

修改前
public void method() {
  int height = 1;
  int width = 2;
  int area = getArea(height, width);
}

private int getArea(int height, int width) {
  return height * width;
}
修改後
public void method() {
  int height = 1;
  int width = 2;
  int area = height * width;
}

另請參閱:擷取為方法

反轉條件 (Invert conditions)

反轉條件中的布林表達式。

範例

讓我們反轉 if 語句中的布林表達式。

修改前
public void method(int value) {
  if (value > 5 && value < 15) {
    // do something
  }
}
修改後
public void method(int value) {
  if (value <= 5 || value >= 15) {
    // do something
  }
}

反轉區域變數 (Invert local variable)

反轉區域布林變數。

範例

讓我們反轉變數 valid

修改前
public void method(int value) {
  boolean valid = value > 5 && value < 15;
}
修改後
public void method(int value) {
  boolean notValid = value <= 5 || value >= 15;
}

移動 (Move)

移動所選元素並修正所有對該元素的參照(包含其他檔案中的參照)。可用的動作包括:

  • 將類別移動到另一個套件
  • 將靜態或執行個體方法移動到另一個類別
  • 將內部類別移動到新檔案

範例

讓我們將靜態方法 print()Office 類別移動到 Printer 類別。

修改前
public class Office {
  public static void main(String[] args) {
    print();
  }

  public static void print() {
    System.out.println("This is printer");
  }

  static class Printer { }
}
修改後
public class Office {
  public static void main(String[] args) {
    Printer.print();
  }

  static class Printer {
    public static void print() {
      System.out.println("This is printer");
    }
  }
}

若靜態方法在其他類別中被使用的頻率高於其自身類別,可對其執行移動重構。

將類別移動到另一個套件。目前不支援從檔案總管執行移動重構。

將內部類別移動到新檔案。

重新命名

預設快速鍵:F2

重新命名所選元素並修正所有對該元素的參照(包含其他檔案中的參照)。

範例

讓我們將類別 Foo 重新命名為 Bar

修改前
public class Foo {
  // ...
}

public void myMethod() {
  Foo myClass = new Foo();
}
修改後
public class Bar {
  // ...
}

public void myMethod() {
  Bar myClass = new Bar();
}

呼叫重新命名重構的快速鍵為 F2。當您在編輯器中的識別項上呼叫該快速鍵時,編輯器內會顯示一個小視窗,您可以在其中更改識別項名稱。按下 Enter 後,所有對該識別項的參照也將一併變更。

檔案總管中的資料夾與檔案也支援重新命名重構。請求變更後,系統會提供受影響檔案的預覽,您可以決定如何套用這些變更。

Rename from Explorer

將解析後的型別變更為 var 型別 (Change resolved type to var type)

使用 var 來宣告區域變數。

範例

修改前
String s = "";
修改後
var s = "";

另請參閱:將 var 型別變更為解析後的型別


將 var 型別變更為解析後的型別 (Change var type to resolved type)

使用解析後的型別來宣告區域變數。

範例

修改前
var s = "";
修改後
String s = "";

另請參閱:將解析後的型別變更為 var 型別

原始碼動作

原始碼動作可用於生成常見的程式碼結構與重複性元素。其中一些屬於「快速修復 (Quick Fixes)」,可協助您在編寫程式碼時即時修正問題。

生成建構函式 (Generate constructors)

為類別新增建構函式。

生成委派方法 (Generate delegate methods)

生成委派方法 (Generate delegate methods)

覆寫/實作方法 (Override/implement methods)

使用此原始碼動作時,系統會以清單方式呈現所有候選項目,您可以決定要覆寫或實作哪些項目。

整理匯入 (Organize imports)

您可以使用此原始碼動作來清理匯入項目。它也能處理模糊的匯入內容,此時會彈出下拉式清單供您選擇正確的項目。同時也會顯示包含未解析型別的程式碼行,協助您進行判斷。

生成 Getter 與 Setter (Generate getters and setters)

您可以批量為所有新成員變數生成 Getter 與 Setter。如果類別擁有多個欄位,原始碼動作將會提示一個「快速選擇 (Quick Pick)」,供您選擇要用來生成存取器方法的目標欄位。

生成 hashCode()equals()

可以生成 hashCode()equals() 的預設實作。系統會列出所有非靜態成員變數,您可以使用核取清單自訂生成的程式碼。

您可以透過以下兩個選項自訂生成的程式碼:

  • 如果您使用 Java 7+,可以將 java.codeGeneration.hashCodeEquals.useJava7Objects 設定為 true,以生成呼叫 Objects.hashObjects.equals 的精簡程式碼。
  • 您也可以將 java.codeGeneration.hashCodeEquals.useInstanceof 設定為 true,以使用 instanceOf 運算子檢查物件型別,而非呼叫 Object.getClass()

生成 toString()

新增了一個用於生成 toString() 方法的原始碼動作。透過所有成員變數的核取清單即可進行自訂。

盡可能將修飾詞改為 final (Change modifiers to final where possible)

final 修飾詞新增至目前原始碼檔案中的所有變數與參數。

範例

修改前
public class Clazz {
  public void method(int value) {
    boolean notValid = value > 5;
    if (notValid) {
      // do something
    }
  }
}
修改後
public class Clazz {
  public void method(final int value) {
    final boolean notValid = value > 5;
    if (notValid) {
      // do something
    }
  }
}

修復無法存取的參照 (Fix non-accessible reference)

此快速修復有助於修復無法存取的參照。

建立不存在的套件 (Create non-existing package)

當您的套件名稱與資料夾名稱不符時,您可以選擇變更原始碼中的套件名稱,或是移動檔案系統中的資料夾(即使目標資料夾尚未建立)。

其他支援的程式碼動作

VS Code 支援的程式碼動作列表不斷增加,上述僅列出最受歡迎的項目。其他值得注意的支援動作包括(但不限於):

  • 建立未解析的型別
  • 移除 final 修飾詞
  • 移除不必要的轉型 (Cast)
  • 移除冗餘的介面
  • 在 switch 語句中新增缺失的 case 標籤
  • 跳轉至 break/continue 的定義處
  • 修正對靜態元素的存取
© . This site is unofficial and not affiliated with Microsoft.