Java 重构和源操作

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

支持的代码操作列表

重构

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

提取为常量

从所选表达式创建静态最终字段并替换字段引用,然后重写出现相同表达式的其他位置。

例子

让我们将 π: 的值提取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()从 class移至Officeclass 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

重命名所选元素并更正对元素的所有引用(也在其他文件中)。

例子

让我们将类重命名FooBar

public class Foo {
  // ...
}

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

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

调用 Rename 重构的快捷方式是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.useJava7Objectstrue生成调用Objects.hash和的较短代码Objects.equals
  • 您还可以设置java.codeGeneration.hashCodeEquals.useInstanceoftrue使用instanceOf运算符来检查对象类型而不是调用Object.getClass().

产生toString()

有一个新的 Source Action 来生成该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 标签
  • 在中断/继续时跳转到定义
  • 正确访问静态元素