Java 重构和源操作
Visual Studio Code 提供了许多重构源代码的选项,以及可在编码时生成代码和修复问题的源操作。要访问它们,请在看到 light bulb
💡 时点击它。或者右键单击编辑器视图并选择源操作...。
支持的代码操作列表
- 重构
- 分配给变量
- 将匿名类转换为嵌套类
- 转换为匿名类创建
- 转换为增强型 for 循环
- 转换为 lambda 表达式
- 转换为静态导入
- 提取重构
- 内联重构
- 反转布尔值
- 移动
- 重命名
- 类型更改
- 源操作
- 支持的其他代码操作
重构
Java 程序重构的目标是在不影响程序行为的情况下进行系统范围的代码更改。VS Code 的 Java 语言支持提供了许多易于访问的重构选项。
调用重构
重构命令可在编辑器的上下文菜单中找到。选择要重构的元素,右键单击打开上下文菜单,然后选择重构...
然后你将看到所有可用的重构选项。
分配给变量
将表达式分配给局部变量或字段。
示例
之前
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 时,对该标识符的所有引用也会随之更改。
文件资源管理器也支持对文件夹和文件进行重命名重构。请求更改后,将提供受影响文件的预览,您可以决定如何应用这些更改。
将解析的类型更改为 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.hash
和Objects.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 的定义
- 更正对静态元素的访问