Эту статью можно рассматривать как краткий обзор c gif-ками по рефакторингам Java-файлов в IDEA для начинающих.
Осторожно, много тяжелых gif-картинок.
"Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” —M. Fowler (1999)
Содержание
- Edit Property Value (без примера)
- - Type Parameter (без примера)
- - Subquery as CTE (без примера)
- Find and Replace Code Duplicate
- Use Interface Where Possible
- Replace Inheritance with Delegation
- Replace Constructor with Factory Method
- Replace Constructor with Builder
- Lombok и Delombok (без примера)
- Internationalize Список источников
Введение
Цель данной статьи - показать доступные способы рефакторинга для Java-файлов (многие способы будут работать и для других языков). Как использовать эти приемы в реальной жизни показано в замечательном видео Тагира Валеева (ссылка в списке источников).
Думаю, каждый, кто работает в IDEA, знает, что в ней куча способов для рефакторинга кода. И почти уверен, что каждый второй смотрит анонсы новой версии, где красиво показаны новые способы рефакторинга и заглядывал в раздел Refaсtor:
Но не уверен, что все точно знают что и как делают все элементы этого списка, хотя они все детально описаны в справки к idea
В статье представлены фрагменты кода, порядок действий и анимации почти для каждого пункта. Также постарался добавить, где возможно, ссылку на замечательную книгу Refactoring: Improving the Design of Existing Code (Martin Fowler). Чтобы не сильно раздувать трафик пришлось довольно сильно обрезать много gif-картинок, поэтому обязательно смотрите использованный код под катом. Горячие клавиши приведены для Windows/LInux по умолчанию.
Раздел «Refaсtor»
Пойдем сверху вниз по порядку.
Пункт «Refactor This» (Ctrl+Alt+Shift+T)
Данный пункт используется для быстрого доступа к списку доступных способов рефакторинга. Заметьте, список зависит от места, где вы его вызываете. Здесь и далее в коде указывает на место каретки в коде, при вызове.
Пункт «Rename» (Shift+F6)
Позволяет переименовать практически любой идентификатор в коде, будь то переменная или названия класса. Изменения распространяются по всему проекту, в некоторых случаях, включая и комментарии. (У Фаулера переименованию посвящено 2 главы - “Rename Field” и “Rename Variable”)
Использованный код
До
public class Main {
public static void main(String[] args) {
System.out.print("Hello");
invo<caret/>ke(", World");
}
private static void invoke(String text) {
//text
System.out.println(text);
}
}
После
public class Main {
public static void main(String[] args) {
System.out.print("Hello");
newFunctionName(", World");
}
private static void newFunctionName(String text) {
//text
System.out.println(text);
}
}
-
Переименование переменной
Использованный код
До
public class Main {
public static void main(String[] args) {
System.out.print("Hello");
invoke(", World");
}
private static void invoke(String te<caret>xt) {
//text
System.out.println(text);
}
}
После
public class Main {
public static void main(String[] args) {
System.out.print("Hello");
invoke(", World");
}
private static void invoke(String newText) {
//newText
System.out.println(newText);
}
}
-
Переименование вложенного класса
Использованный код
До
public class Main {
public static void main(String[] args) {
System.out.print("Hello");
invoke(", World");
}
private static void invoke(String text) {
//text
System.out.println(text);
throw new MyExc<caret>eption();
}
public static class MyException extends RuntimeException {
}
}
После
public class Main {
public static void main(String[] args) {
System.out.print("Hello");
invoke(", World");
}
private static void invoke(String text) {
//text
System.out.println(text);
throw new NewMyException ();
}
public static class NewMyException extends RuntimeException {
}
}
-
Переименование класса
Использованный код
До
public class Main {
public static void main(String[] args) {
MyS<caret>ervice service = new MyService();
service.service();
}
}
После
public class Main {
public static void main(String[] args) {
NewMyService myService = new NewMyService ();
myService.service();
}
}
-
Переименование пакета
Использованный код
package gen<caret>eral;
public class Main {
public static void main(String[] args) {
NewMyService service = new NewMyService();
service.service();
}
}
После
package org.test.java.src;
public class Main {
public static void main(String[] args) {
NewMyService service = new NewMyService();
service.service();
}
}
Пункт «Rename File»
Переименовывает файл и ссылки на этот файл. В принципе можно вызывать через Shift+F6 если выделен файл. В диалоговом окне можно указать область поиска для переименований (Scope), искать ли ссылки или в комментариях и строчках
Использованный код
До
public class Main {
public static void main(String[] args) throws IOException {
Path path = Paths.get("src/general/TestFile.txt");
String read = Files.readAllLines(path).get(0);
System.out.println(read);
}
}
После
public class Main {
public static void main(String[] args) throws IOException {
Path path = Paths.get("src/general/TestFile2.txt");
String read = Files.readAllLines(path).get(0);
System.out.println(read);
}
}
Пункт «Change Signature» (Ctrl+F6)
У Фаулера этому посвящена глава “Change Function Declaration”. В новой версии IDEA «Change Signature» был немного доработан. Я знаю два пути:
-
первый путь - прямой - изменить сигнатуру метода и вызвать обновление,
-
второй - через диалоговое окно.
Пример для первого способа (через изменения сигнатуры метода)
Изменяем сигнатуру метода. В этот момент слева появляется "R" и в контекстном меню появляется пункт "Update usages to reflect signature change", который позволяет обновить все использования метода.
Использованный код
До
public class ChangeSignature {
public static void main(String[] args) {
invokeMethod("Hello");
invokeMethod("World");
}
private static void invokeMethod(String text) {
System.out.println(text);
}
}
После
public class ChangeSignature {
public static void main(String[] args) {
invokeMethod("Hello", null);
invokeMethod("World", null);
}
private static void invokeMethod(String text, String newType) {
System.out.println(text);
}
}
Пример для второго способа (через диалоговое окно)
В диалоговом окне можно изменить состав переменных, exception, и даже сгенерировать переопределенный метод.
Использованный код
До
public class ChangeSignature {
public static void main(String[] args) {
invokeMethod("Hello");
invokeMethod("World");
}
private static void invokeMethod(String<caret> text) {
System.out.println(text);
}
}
После
public class ChangeSignature {
public static void main(String[] args) {
invokeMethod("Hello");
invokeMethod("World");
}
private static void invokeMethod(String text) {
invokeMethod(text, null);
}
private static void invokeMethod(String text, String newName) {
System.out.println(text);
}
}
Пункт «Edit Property Value» (Alt + F6)
На данный момент (Idea 2020.2) экспериментальная функция и по умолчанию не включена. Включить можно параметром property.value.inplace.editing=true Поэтому примеры не привожу.
Пункт «Type Migration» (Ctrl + Shift + F6)
Позволяет изменить тип переменной, включая сигнатуры методов, возвращаемый тип переменной.
Использованный код
До
public class ChangeSignature {
public static void main(String[] args) {
Inte<caret>ger hello = 1;
print(hello);
}
private static void print(Integer text) {
System.out.println(text);
}
}
После
public class ChangeSignature {
public static void main(String[] args) {
Number hello = 1;
print(hello);
}
private static void print(Number text) {
System.out.println(text);
}
}
Пункт «Make Static» (Ctrl + Shift + F6)
Позволяет сконвертировать метод или внутренний класс в статический. (Противоположность Convert To Instance Method)
Использованный код
До
public class MakeStatic {
public static void main(String[] args) {
MakeStatic makeStatic = new MakeStatic();
makeStatic.sayHello();
}
public void say<caret>Hello() {
System.out.println("Hello, World");
}
}
После
public class MakeStatic {
public static void main(String[] args) {
MakeStatic makeStatic = new MakeStatic();
MakeStatic.sayHello();
}
public static void sayHello() {
System.out.println("Hello, World");
}
}
Пункт «Convert To Instance Method»
Позволяет сконвертировать статический метод в нестатический (противоположность ”Make Static”). При этом можно указать к какому классу будет относится новый метод.
Использованный код
До
public class MakeStatic {
public static void main(String[] args) {
sayHello();
}
public static void sa<caret>yHello() {
System.out.println("Hello, World");
}
}
После
public class MakeStatic {
public static void main(String[] args) {
new MakeStatic().sayHello();
}
public void sayHello() {
System.out.println("Hello, World");
}
}
Пункт «Move Classes» (F6)
В принципе делает, что и написано, перемещает классы.
Использованный код
До
package org.example.test.service;
public class TestService {
<caret>
}
После
package org.example.test;
public class TestService {
}
Пункт «Copy Classes» (F5)
Многие программисты любят копировать файлы, а не начинать с чистого листа. Для этого прекрасно подходит F5. Меняем название на нужноe, указываем пакет и готово.
Пункт «Safe Delete» (Alt+Delete)
По функциональности почти повторяет то, что можно получить через контекстное меню (Alt + Enter), но позволяет удалять чуть больше. Поэтому, я заметил, у многих знакомых любимый способ рефакторинга - F2(следующая ошибка) и Alt + Enter или Alt + Delete. Можно удалять классы, переменные, методы. Перед удалением IDEA выполнит поиск использования удаляемых элементов, и если IDEA найдет, что они где-то используется покажет диалоговое окно Usages Detected. Про удаление неиспользуемого кода у Фаулера есть целая глава - “Remove Dead Code”
Использованный код
До
package org.example.test;
public class MainClass {
public static void main(String[] args) {
start();
}
private static void start() {
String unUsedVariable;
System.out.println("Hello, World!");
}
private static void unUsedMethod() {
}
}
После
<empty>
Пункт «Extract/Introduce»
Следующий блок - Extract/Introduce. Думаю, является одним из самых популярных. Позволяет извлекать разные части программы.
Пункт «Variable» (Ctrl+Alt+V)
Создает новую переменную из выделенного фрагмента. (Этому способу у Фаулера посвящена глава “Extract Variable”).
Использованный код
До
public class ExtractVariable {
public static void main(String[] args) {
sayHello();
}
private static void sayHello() {
System.out.println("He<caret>llo, World!");
}
}
После
public class ExtractVariable {
public static void main(String[] args) {
sayHello();
}
private static void sayHello() {
String text = "Hello, World!";
System.out.println(text);
}
}
Пункт «Constant» (Ctrl+Alt+C)
Создает новую константу из выделенного фрагмента.
Использованный код
До
public class ExtractVariable {
public static void main(String[] args) {
sayHello();
}
private static void sayHello() {
System.out.println("He<caret>llo, World!");
}
}
После
public class ExtractVariable {
public static final String HELLO_WORLD = "Hello, World!";
public static void main(String[] args) {
sayHello();
}
private static void sayHello() {
System.out.println(HELLO_WORLD);
}
}
Пункт «Field» (Ctrl+Alt+F)
Создает новое поле класса из выделенного фрагмента.
Использованный код
До
public class ExtractVariable {
public static void main(String[] args) {
sayHello();
}
private static void sayHello() {
System.out.println("He<caret>llo, World!");
}
}
После
public class ExtractVariable {
private static String x;
public static void main(String[] args) {
sayHello();
}
private static void sayHello() {
x = "Hello, World!";
System.out.println(x);
}
}
Пункт «Parameter» (Ctrl+Alt+P)
Создает новый параметр (функции) из выделенного фрагмента.
Использованный код
До
public class ExtractVariable {
public static void main(String[] args) {
sayHello();
}
private static void sayHello() {
System.out.println("He<caret>llo, World!");
}
}
После
public class ExtractVariable {
public static void main(String[] args) {
sayHello("Hello, World!");
}
private static void sayHello(String x) {
System.out.println(x);
}
}
Пункт «Functional Parameter»
Очень похож на пункт «Parameter», но теперь в функцию мы передаем или java.util.function.Supplier, или javafx.util.Builder. Обратите внимание, данный рефакторинг может привести к нежелательным эффектам.
Использованный код
До
public class ExtractParameter {
public static void main(String[] args) {
System.out.println(generateText());
}
private static String generateText() {
return "Hello, Wor<caret>ld!".toUpperCase();
}
}
После
public class ExtractParameter {
public static void main(String[] args) {
System.out.println(generateText(() -> "Hello, World!"));
}
private static String generateText(final Supplier<string> getText) {
return getText.get().toUpperCase();
}
}
Пункт «Functional Variable»
Очень похож на пункт «Variable», но теперь мы получаем или java.util.function.Supplier или javafx.util.Builder.
Использованный код
До
public class ExtractParameter {
public static void main(String[] args) {
System.out.println(generateText());
}
private static String generateText() {
return "Hello, W<caret>orld!".toUpperCase();
}
}
После
public class ExtractParameter {
public static void main(String[] args) {
System.out.println(generateText());
}
private static String generateText() {
Supplier<string> getText = () -> "Hello, World!";
return getText.get().toUpperCase();
}
}
Пункт «Parameter Object»
Удобный способ, когда в функцию передается много аргументов и вам надо обернуть их в класс. (У Фаулера это глава “Introduce Parameter Object”).
Использованный код
До
public class ExtractParameter {
public static void main(String[] args) {
System.out.println(generateText("Hello", "World!"));
}
private static String generateText(Str<caret>ing hello, String world) {
return hello.toUpperCase() + world.toUpperCase();
}
}
После
public class ExtractParameter {
public static void main(String[] args) {
System.out.println(generateText(new HelloWorld("Hello", "World!")));
}
private static String generateText(HelloWorld helloWorld) {
return helloWorld.getHello().toUpperCase() + helloWorld.getWorld().toUpperCase();
}
private static class HelloWorld {
private final String hello;
private final String world;
private HelloWorld(String hello, String world) {
this.hello = hello;
this.world = world;
}
public String getHello() {
return hello;
}
public String getWorld() {
return world;
}
}
}
Пункт «Method» (Ctrl+Alt+M)
Извлекаем метод из выделенного фрагмента. (У Фаулера есть глава про похожий способ рефакторинга - “Extract Function”).
Использованный код
До
public class ExtractMethod {
public static void main(String[] args) {
String text = "Hello, World!";
System.out.prin<caret>tln(text);
}
}
После
public class ExtractMethod {
public static void main(String[] args) {
String text = "Hello, World!";
print(text);
}
private static void print(String text) {
System.out.println(text);
}
}
Пункт «Type Parameter»
Рефакторинг из мира Kotlin, и для Java не применим (буду рад добавить, если кто-то сделает пример).
Пункт «Replace Method With Method Object»
Оборачивает выделенный фрагмент в объект. Может использоваться, если надо вернуть несколько объектов из метода (возвращает объект-обертку).
Использованный код
До
public class ExtractMethod {
public static void main(String[] args) {
String text = "Hello, World!";
print(text);
}
private static void print(String text) {
System.out.p<caret>rintln(text);
}
}
После
public class ExtractMethod {
public static void main(String[] args) {
String text = "Hello, World!";
print(text);
}
private static void print(String text) {
new Printer(text).invoke();
}
private static class Printer {
private String text;
public Printer(String text) {
this.text = text;
}
public void invoke() {
System.out.println(text);
}
}
}
Пункт «Delegate»
Позволяет извлечь методы и поля в отдельный класс.
Использованный код
До
public class Delegate {
public static void main(String[] args) {
new Delegate().print();
}
private void print() {
System.ou<caret>t.println("Hello, World!");
}
}
После
public class Delegate {
private final Printer printer = new Printer();
public static void main(String[] args) {
new Delegate().print();
}
private void print() {
printer.print();
}
public static class Printer {
public Printer() {
}
private void print() {
System.out.println("Hello, World!");
}
}
}
Пункт «Interface»
Для заданного класса и его методов создает интерфейс. (Особенно удобно, когда при работе со Spring, когда кто-то забыл для компонента создать соответствующий интерфейс)
Использованный код
До
public class ExtractImpl {
public static void main(String[] args) {
new ExtractImpl().print();
}
public void print() {
System.out.println("Hello, World!");
}
}
После
public class ExtractImpl implements ExtractInterface {
public static void main(String[] args) {
new ExtractImpl().print();
}
@Override
public void print() {
System.out.println("Hello, World!");
}
}
public interface ExtractInterface {
void print();
}
Пункт «Superclass»
Аналогично пункту «Interface», только теперь создается класс-родитель (Superclass). Фаулер описывает этот способ рефакторинга в главе “Extract Superclass”.
Использованный код
До
public class ExtractImpl {
public static void main(String[] args) {
new ExtractImpl().print();
}
public void print() {
System.out.println("Hello, World!");
}
}
После
public class ExtractImpl extends ExtractAbstr {
public static void main(String[] args) {
new ExtractImpl().print();
}
}
public class ExtractAbstr {
public void print() {
System.out.println("Hello, World!");
}
}
Пункт «Subquery as CTE»
Относится к Sql, поэтому пропускаю. Если кто-то пришлет пример, хотя бы в виде кода - с удовольствием дополню.
Пункт «RSpec 'let'»
Относится к Ruby, поэтому пропускаю Если кто-то пришлет пример, хотя бы в виде кода - с удовольствием дополню.
Пункт «Inline»
Возможно один из самых крутых методов рефакторинга, Инлайнить можно почти все. Фаулер описывает этот способ рефакторинга в главах “Inline Class”, “Inline Function”, “Inline Variable”.
Использованный код
До
public class Inline {
public static void main(String[] args) {
print();
}
private static void print() {
new Printer().print();
}
private static class Printer {
public void print() {
String text = "Hello, World!";
System.out.println(t<caret>ext);
}
}
}
После
public class Inline {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
Пункт «Find and Replace code duplicate»
Ищет похожие фрагменты кода и предлагает заменить их, например, вызовом метода или константой.
Использованный код
До
public class Replace {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
public void print() {
System.out.println("Hello, World!");
}
public void print2() {
System.out.prin<caret>tln("Hello, World!");
}
}
После
public class Replace {
public static void main(String[] args) {
print2();
}
public void print() {
print2();
}
public static void print2() {
System.out.println("Hello, World!");
}
}
Пункт «Invert Boolean»
Позволяет инвертировать булевые переменные.
Использованный код
До
public class Invert {
public static void main(String[] args) {
boolean co<caret>ndition = true;
if (condition) {
System.out.println("Hello, World!");
}
}
}
После
public class Invert {
public static void main(String[] args) {
boolean condition = false;
if (!condition) {
System.out.println("Hello, World!");
}
}
}
Пункт «Pull Member Up»
Позволяет перемещать методы или поля по иерархии вверх. Зачем это нужно написано у Фаулера в главах “Pull Up Field” и “Pull Up Method”. Выполняет обратную задачу пункта «Pull Member Down».
Использованный код
До
public class PullMethod {
public static void main(String[] args) {
new InnerClass().print();
}
private static class InnerClass extends AbstClass {
public void print() {
System.out.pri<caret>ntln("Hello, World");
}
}
private static abstract class AbstClass {
}
}
После
public class PullMethod {
public static void main(String[] args) {
new InnerClass().print();
}
private static class InnerClass extends AbstClass {
}
private static abstract class AbstClass {
public void print() {
System.out.println("Hello, World");
}
}
}
Пункт «Pull Member Down»
Выполняет обратную задачу пункта «Pull Member Up». Позволяет перемещать методы или поля по иерархии вниз. (У Фаулера - глава “Push Down Method”)
Использованный код
До
public class PullMethod {
public static void main(String[] args) {
new InnerClass().print();
}
private static class InnerClass extends AbstClass {
}
private static abstract class AbstClass {
public void print() {
System.out.prin<caret>tln("Hello, World");
}
}
}
После
public class PullMethod {
public static void main(String[] args) {
new InnerClass().print();
}
private static class InnerClass extends AbstClass {
@Override
public void print() {
System.out.println("Hello, World");
}
}
private static abstract class AbstClass {
public abstract void print();
}
}
Пункт «Push ITds In»
Используется при работе с AsperctJ.
Использованный код
До
aspect myAspect {
boolean Account.closed = <caret>false;
void Account.close() {
closed = true;
}
}
class Account {
}
После
aspect myAspect {
boolean Account.closed = false;
}
class Account {
void close() {
closed = true;
}
}
Пункт «Use Interface Where Possible»
IDEA старается заменить, где это возможно, указания классов на указание интерфейсов.
Использованный код
До
public class ExtractInterface {
public static void main(String[] args) {
InnerClass innerClass = new InnerClass();
print(innerClass);
}
private static void print(InnerClass innerClass) {
innerClass.print();
}
private static class InnerClass implements InnerInterface{
@Override
public void print() {
System.out.println("Hello, World!");
}
}
private static interface InnerInterface{
void print();
}
}
После
public class ExtractInterface {
public static void main(String[] args) {
InnerInterface innerClass = new InnerClass();
print(innerClass);
}
private static void print(InnerInterface innerClass) {
innerClass.print();
}
private static class InnerClass implements InnerInterface{
@Override
public void print() {
System.out.println("Hello, World!");
}
}
private static interface InnerInterface{
void print();
}
}
Пункт «Replace Inheritance with Delegation»
Заменяет наследование делегированием. У Фаулера про это главы “Replace Subclass with Delegate” и “Replace Superclass with Delegate”.
Использованный код
До
public class InheritanceDelegation {
public static void main(String[] args) {
InnerClass innerClass = new InnerClass();
print(innerClass);
}
private static void print(InnerClass innerClass) {
innerClass.print();
}
private static class In<caret>nerClass extends AbstractClass {
}
private static class AbstractClass {
public void print() {
System.out.println("Hello, World!");
}
}
}
После
public class InheritanceDelegation {
public static void main(String[] args) {
InnerClass innerClass = new InnerClass();
print(innerClass);
}
private static void print(InnerClass innerClass) {
innerClass.print();
}
private static class InnerClass {
private final AbstractClass abstractClass = new AbstractClass();
public void print() {
abstractClass.print();
}
}
private static class AbstractClass {
public void print() {
System.out.println("Hello, World!");
}
}
}
Пункт «Remove Middleman»
Заменяет все делегированные вызовы на прямые. (У Фаулера - глава “Remove Middle Man”).
Использованный код
До
public class Middleman {
public static void main(String[] args) {
InnerClass innerClass = new InnerClass();
innerClass.print();
}
private static class InnerClass {
private final NextClass next<caret>Class = new NextClass();
public void print() {
nextClass.print();
}
}
private static class NextClass {
public void print() {
System.out.println("Hello, World!");
}
}
}
После
public class Middleman {
public static void main(String[] args) {
InnerClass innerClass = new InnerClass();
innerClass.getNextClass().print();
}
private static class InnerClass {
private final NextClass nextClass = new NextClass();
public NextClass getNextClass() {
return nextClass;
}
}
private static class NextClass {
public void print() {
System.out.println("Hello, World!");
}
}
}
Пункт «Wrap Method Return Value»
Оборачивает возвращаемое значение в объект-обертку. Удобно, когда нужно возвращать несколько связанных значений.
Использованный код
До
public class WrapMethodReturnValue {
public static void main(String[] args) {
System.out.println(new MessageFolder().get());
}
private static class MessageFolder {
public String get() {
ret<caret>urn "Hello, World!";
}
}
}
После
public class WrapMethodReturnValue {
public static void main(String[] args) {
System.out.println(new MessageFolder().get().getValue());
}
private static class MessageFolder {
public Message get() {
return new Message("Hello, World!");
}
public class Message {
private final String value;
public Message(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
}
}
Пункт «Encapsulate Field»
Скрывает поле за getter, setter.
Использованный код
До
public class EncapsulateField {
public static void main(String[] args) {
System.out.println(new InnerClass().message);
}
private static class InnerClass {
public String m<caret>essage = "Hello, World!";
}
}
После
public class EncapsulateField {
public static void main(String[] args) {
System.out.println(new InnerClass().getMessage());
}
private static class InnerClass {
private String message = "Hello, World!";
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
}
Пункт «Replace Temp with Query»
Пусть у вас есть
int size = getActualSize()
с помощью этого способа рефакторинга вы можете заменить использование size на использование метода getActualSize(). Это приведет к увеличению количества вызовов методов, но в некоторых случаях может оказаться полезным. Пример так же показывает возможность извлечения метода с одновременным его дальнейшим использованием.
Использованный код
До
public class ReplaceTemp {
public static void main(String[] args) {
String hello = "Hello";
String mes<caret>sage = hello + ", World!";
System.out.println(message);
}
}
После
public class ReplaceTemp {
public static void main(String[] args) {
String hello = "Hello";
System.out.println(message(hello));
}
private static String message(String hello) {
return hello + ", World!";
}
}
Пункт «Replace Constructor with Factory Method»
Генерирует фабричный метод для указанного конструктора. Идеально, если у вас нет Lombok. (У Фаулера этому посвящена глава “Replace Constructor with Factory Function”).
Использованный код
До
public class ReplaceConstructor {
public static void main(String[] args) {
new InnerClass("Hello", "World").print();
}
private static class InnerClass {
private String message;
public Inner<caret>Class(String hello, String world) {
message = hello + ", " + world;
}
public void print() {
System.out.println(message);
}
}
}
После
public class ReplaceConstructor {
public static void main(String[] args) {
InnerClass.createInnerClass("Hello", "World").print();
}
private static class InnerClass {
private String message;
private InnerClass(String hello, String world) {
message = hello + ", " + world;
}
public static InnerClass createInnerClass(String hello, String world) {
return new InnerClass(hello, world);
}
public void print() {
System.out.println(message);
}
}
}
Пункт «Replace Constructor with Builder»
Генерирует builder для указанного конструктора. Идеально, если у вас нет Lombok.
Использованный код
До
public class ReplaceConstructor {
public static void main(String[] args) {
new InnerClass("Hello", "World").print();
}
private static class InnerClass {
private String message;
public InnerC<caret>lass(String hello, String world) {
message = hello + ", " + world;
}
public void print() {
System.out.println(message);
}
}
}
После
public class ReplaceConstructor {
public static void main(String[] args) {
new InnerClassBuilder().setHello("Hello").setWorld("World").createInnerClass().print();
}
static class InnerClass {
private String message;
public InnerClass(String hello, String world) {
message = hello + ", " + world;
}
public void print() {
System.out.println(message);
}
}
}
public class InnerClassBuilder {
private String hello;
private String world;
public InnerClassBuilder setHello(String hello) {
this.hello = hello;
return this;
}
public InnerClassBuilder setWorld(String world) {
this.world = world;
return this;
}
public ReplaceConstructor.InnerClass createInnerClass() {
return new ReplaceConstructor.InnerClass(hello, world);
}
}
Пункт «Generify»
Пытается код с raw-типами превратить в код с Generic-типами. Актуален при миграции с java версий ранее 1.5 на современные версии.
Использованный код
До
public class Generify {
public static void main(String[] args) {
List list = getList();
Object message = list.get(0);
System.out.println(message);
}
private static List getList() {
ArrayList arrayList = new ArrayList();
arrayList.add("Hello, World!");
return arrayList;
}
}
После
public class Generify {
public static void main(String[] args) {
List<string> list = getList();
String message = list.get(0);
System.out.println(message);
}
private static List<string> getList() {
ArrayList<string> arrayList = new ArrayList<>();
arrayList.add("Hello, World!");
return arrayList;
}
}
Пункт «Migrate»
Предоставляет готовые миграции для следующего списка:
А также предоставляет возможность делать свои. Вот, например, правила миграции для JUnit(4.x -> 5.0):
Вот здесь есть подробное видео про миграцию для JUnit(4.x -> 5.0).
Пункт «Lombok» и «Delombok»
Предоставляются плагином “Lombok”. Недавно было объявлено, что теперь он будет входить в стандартную поставку IDEA. Используется при работе с библиотекой кодогенерации “Lombok”.
Пункт «Internationalize»
Используется для интернационализации. К сожалению, в справке не нашел на данный момент информации. IDEA сейчас активно локализуется на другие языки, скорее всего для этого был разработан этот метод.
Список источников
-
Прекрасный доклад про атомарный рефакторинг от Тагира Валеева
-
Refactoring: Improving the Design of Existing Code (Martin Fowler)
Автор: pyltsinm