Java のリファクタリングとソースアクション
Visual Studio Code には、ソースコードをリファクタリングするための多くのオプションと、コーディング中にコードを生成したり問題を修正したりするためのソースアクションが用意されています。これらにアクセスするには、電球
💡アイコンが表示されたらクリックしてください。または、エディタービューを右クリックして、ソースアクション... を選択してください。
サポートされているコードアクションのリスト
- リファクタリング
- 変数に代入
- 匿名クラスをネストされたクラスに変換
- 匿名クラスの作成に変換
- 拡張 for ループに変換
- ラムダ式に変換
- 静的インポートに変換
- リファクタリングの抽出
- インラインリファクタリング
- ブール値を反転
- 移動
- 名前変更
- 型変更
- ソースアクション
- サポートされているその他のコードアクション
リファクタリング
Java プログラムのリファクタリングの目的は、プログラムの動作に影響を与えることなく、システム全体のコード変更を行うことです。VS Code 用 Java Language Support は、多くの簡単にアクセスできるリファクタリングオプションを提供します。
リファクタリングの呼び出し
リファクタリングコマンドは、エディターのコンテキストメニューから利用できます。リファクタリングする要素を選択し、右クリックしてコンテキストメニューを開き、リファクタリング... を選択します。
すると、利用可能なすべてのリファクタリングオプションが表示されます。
変数に代入
式をローカル変数またはフィールドに代入します。
例
変更前
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);
}
}
匿名クラスの作成に変換
ラムダ式を匿名クラスの作成に変換します。
例
変数 runnable
にはラムダ式が代入されています。これを匿名クラスの作成に変換してみましょう。
変更前
public void method() {
Runnable runnable = () -> {
// do something
};
}
変更後
public void method() {
Runnable runnable = new Runnable() {
@Override
public void run() {
// do something
}
};
}
参考: ラムダ式に変換
拡張 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
}
}
ラムダ式に変換
匿名クラスの作成をラムダ式に変換します。
例
匿名クラス Runnable(){...}
をラムダ式に変換してみましょう。
変更前
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 型に変更
ソースアクション
ソースアクションは、一般的なコード構造と反復要素を生成するために使用できます。それらのいくつかは、コードの問題をその場で修正するのに役立つクイックフィックスです。
コンストラクターの生成
クラスのコンストラクターを追加します。
デリゲートメソッドの生成
デリゲートメソッドの生成
メソッドのオーバーライド/実装
このソースアクションを使用すると、すべての候補がチェックリスト付きで表示されます。その後、オーバーライドまたは実装するものを決定できます。
インポートの整理
このソースアクションを使用して、インポートを整理できます。あいまいなインポートを処理することもでき、その場合は、適切なものを選択するためのドロップダウンリストが表示されます。未解決の型を持つコード行も、決定を支援するために表示されます。
ゲッターとセッターの生成
すべての新しいメンバー変数のゲッターとセッターを一括生成できます。クラスに複数のフィールドがある場合、ソースアクションは、アクセサーメソッドの生成に使用するターゲットフィールドを選択するためのクイックピックを促します。
hashCode()
および equals()
の生成
hashCode()
および equals()
は、デフォルトの実装で生成できます。すべての非静的メンバー変数がリストされ、チェックリストを使用して生成されたコードをカスタマイズできます。
生成されたコードをカスタマイズするための 2 つのオプションがあります。
- Java 7 以降を使用している場合は、
java.codeGeneration.hashCodeEquals.useJava7Objects
をtrue
に設定して、Objects.hash
およびObjects.equals
を呼び出す短いコードを生成できます。 - また、
java.codeGeneration.hashCodeEquals.useInstanceof
をtrue
に設定して、Object.getClass()
を呼び出す代わりにinstanceOf
演算子を使用してオブジェクト型をチェックすることもできます。
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 での定義へのジャンプ
- 静的要素へのアクセスを修正