现已发布!阅读 10 月份的新功能和修复。

Java 重构和源代码操作

Visual Studio Code 提供了许多重构源代码的选项,以及在编码时生成代码和修复问题的源代码操作。要访问它们,请在看到 light bulb 💡 时单击它。或者右键单击编辑器视图,然后选择 **源代码操作...**。

支持的代码操作列表

重构

Java 程序重构的目标是在不影响程序行为的情况下进行系统范围的代码更改。VS Code 的 Java 语言支持提供了许多易于访问的重构选项。

调用重构

重构命令可从编辑器的上下文菜单中获得。选择要重构的元素,右键单击以打开上下文菜单,然后选择 **重构...**。

Invoke Refactoring

然后,您将看到所有可用的重构选项。

分配给变量

将表达式分配给局部变量或字段。

示例

之前
Arrays.asList("apple", "lemon", "banana");
之后
List<String> fruits = Arrays.asList("apple", "lemon", "banana");

它还可以用于将参数分配给构造函数中未使用的参数的新字段。

将匿名类转换为嵌套类

将匿名内部类转换为成员类。

示例

让我们将匿名类 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);
  }
}

转换为匿名类创建

将 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 循环

将简单的 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 表达式

将匿名类创建转换为 lambda 表达式。

示例

让我们将匿名类 Runnable(){...} 转换为 lambda 表达式。

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

另请参见:转换为匿名类创建

转换为静态导入

将字段或方法转换为静态导入。

示例

让我们将 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);
}

提取为常量

从选定表达式创建一个静态 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;
}

另请参见:内联常量

提取为字段

声明一个新的字段,并使用选定的表达式对其进行初始化。原始表达式将被字段的使用替换。

示例

让我们将变量 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;
  }
}

当选择变量声明时,将变量转换为字段。

提取为方法

创建一个包含当前选择语句或表达式的新方法,并使用对新方法的引用替换选择。此功能对于清理冗长、混乱或过于复杂的方法很有用。

示例

让我们将表达式 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;
}

另请参见:内联方法

提取为局部变量

创建一个新变量,并将其分配给当前选定的表达式,并使用对新变量的引用替换选择。

示例

让我们将表达式 platform.equalsIgnoreCase("MAC") 提取为新变量。

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

提取后,您还可以在同一事务中执行重命名。

另请参见:内联局部变量

内联常量

用其定义的值替换常量引用。

示例

让我们将常量 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;
}

另请参见:提取为常量

内联局部变量

用其初始化器替换冗余的变量使用。

示例

让我们将变量 isMac 直接替换为布尔表达式。

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

另请参见:提取为局部变量

内联方法

用方法的主体替换对方法的调用。

示例

让我们将方法 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;
}

另请参见:提取为方法

反转条件

反转条件中的布尔表达式。

示例

让我们反转 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
  }
}

反转局部变量

反转局部布尔变量。

示例

让我们反转变量 valid

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

移动

移动选定的元素并更正对元素的所有引用(包括其他文件中的引用)。可用的操作是

  • 将类移动到另一个包
  • 将静态或实例方法移动到另一个类
  • 将内部类移动到新文件

示例

让我们将静态方法 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 类型

使用 var 声明局部变量。

示例

之前
String s = "";
之后
var s = "";

另请参见:将 var 类型更改为解析后的类型


将 var 类型更改为解析后的类型

使用解析后的类型声明局部变量。

示例

之前
var s = "";
之后
String s = "";

另请参见:将解析后的类型更改为 var 类型

源代码操作

源代码操作可用于生成常见的代码结构和重复元素。其中一些是快速修复,可以帮助您即时修复代码问题。

生成构造函数

为类添加构造函数。

生成委托方法

生成委托方法

重写/实现方法

使用此源代码操作,所有候选项将以清单的形式呈现给您。然后,您可以决定要重写或实现哪些内容。

整理导入

您可以使用此源代码操作来清理您的导入。它还可以处理模糊的导入,在这种情况下,将为您提供一个下拉列表供您选择正确的导入。还将向您展示包含未解析类型的代码行,以帮助您做出决定。

生成 getter 和 setter

您可以批量生成所有新成员变量的 getter 和 setter。如果类具有多个字段,则源代码操作将提示您进行快速选择,以选择要用于生成访问器方法的目标字段。

生成 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

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
    }
  }
}

修复不可访问的引用

此快速修复有助于您修复不可访问的引用。

创建不存在的包

当您的包名与文件夹名不匹配时,您可以选择在源代码中更改包名,或在文件系统中移动文件夹(即使目标文件夹尚不存在)。

其他支持的代码操作

VS Code 支持的代码操作列表不断增长,上面仅列出了最流行的操作。其他值得注意的受支持操作包括(但不限于)

  • 创建未解析的类型
  • 删除 final 修饰符
  • 删除不必要的强制转换
  • 删除冗余接口
  • 在 switch 语句中添加缺失的 case 标签
  • 在 break/continue 上跳转到定义
  • 更正对静态元素的访问