Java 重構與原始碼動作
Visual Studio Code 提供了許多重構原始碼的選項,以及在編寫程式碼時用於生成程式碼和修復問題的「原始碼動作」。若要使用這些功能,請在看到燈泡 💡 圖示時點擊它,或者在編輯器視窗中按右鍵並選擇原始碼動作... (Source Action...)。
支援的程式碼動作列表
- 重構
- 指派給變數 (Assign to variable)
- 將匿名類別轉換為巢狀類別 (Convert anonymous to nested class)
- 轉換為匿名類別實例化 (Convert to anonymous class creation)
- 轉換為增強型 for 迴圈 (Convert to enhanced for loop)
- 轉換為 Lambda 表達式 (Convert to lambda expression)
- 轉換為靜態匯入 (Convert to static import)
- 擷取重構 (Extract refactorings)
- 內聯重構 (Inline refactorings)
- 反轉布林值 (Invert boolean)
- 移動 (Move)
- 重新命名
- 型別變更 (Type change)
- 原始碼動作
- 其他支援的程式碼動作
重構
Java 程式重構的目標是在不影響程式行為的前提下,進行系統性的程式碼變更。VS Code 的 Java 語言支援功能提供了許多易於使用的重構選項。
呼叫重構
重構指令可透過編輯器的右鍵選單取得。選擇您想要重構的元素,按右鍵開啟選單,然後選擇重構... (Refactor...)。

接著您將會看到所有可用的重構選項。
指派給變數 (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 後,所有對該識別項的參照也將一併變更。
檔案總管中的資料夾與檔案也支援重新命名重構。請求變更後,系統會提供受影響檔案的預覽,您可以決定如何套用這些變更。

將解析後的型別變更為 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.hash與Objects.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 的定義處
- 修正對靜態元素的存取