Strukturalne dodanie pola serialVersionUID w IntelliJ
Zadeklarowanie pola serialVersionUID
w klasie implementującej interfejs Serializable pozwala zapewnić lepszą kontrolę
nad kompatybilnością klas w Javie. O praktyce tej więcej poczytać możesz tutaj.
W IntelliJ IDE przeglądając inspekcje kodu znajdziesz właśnie regułę "Serializable class without "serialVersionUID", którą możesz włączyć i zastosować szybką poprawkę (ALT+Enter na klasie).
Potrzeba nagłej zmiany wszystkich klas w celu dodania tego pola może okazać się dosyć męczącym zadaniem. Szybkiej poprawki nie można zastosować zbiorczo (przynajmniej w obecnej wersji, czyli 222.3153.4). Patrząc na implementację, brakuje tutaj również wykorzystania standardowego interfejsu, który mógłby być użyty do automatycznego czyszczenia kodu przez IntelliJ.
Wyszukiwanie strukturalne i podmiana
Istnieje jednak inna funkcjonalność w IntelliJ, której można użyć do szybkiego dodania brakującego pola w wybranych klasach. Structural Search/Replace to mechanizm wyszukiwania i zastępowania części kodu zgodnie ze składnią wybranego języka wspieranego przez IDE. Funkcjonalność tę wywołasz za pomocą szybkich akcji (CTRL+SHIFT+A) lub menu: Edit > Find > Replace Structurally.
W pierwszej kolejności spójrz na kilka przykładów prezentowanych przez IntelliJ, aby zapoznać się z tą funkcjonalnością. Chociaż samo wyszukiwanie strukturalne jest naprawdę użyteczne, to zastąpienie nie zawsze działa tak jakby chciał tego użytkownik. Tworząc szablon z elementem interfejsu jak wyżej, szybko przekonasz się, że zastąpienie w klasach usunie interfejsy niezgodne z szablonem:
// Przed
public class Foo extends AbstractClass implements IFooSerializable, IFoo {}
public class Bar extends AbstractClass implements Serializable, IFoo {}
// Po
public class Foo extends AbstractClass {}
public class Bar extends AbstractClass implements Serializable {}
Aby osiągnąć pożądane rezultaty, ogranicz kryteria wyszukiwania do samej klasy. W ten sposób interfejsy pozostaną nienaruszone. Strukturalne wyszukiwanie i zamiana zapewnia dostęp do zaawansowanego interfejsu IntelliJ, nie jest to jednak proste podejście. Jeszcze raz spójrz na implementację. W skrypcie (Groovy) wyszukiwania klasy możesz zaimplementować tę samą logikę. Wszystko, co musisz wiedzieć, to to, że kontekstem wyszukiwania jest interfejs PsiClass.
!__context__.interface &&
!__context__.enum &&
!__context__.record &&
__context__.qualifiedName != null &&
__context__.isInheritor(com.intellij.psi.JavaPsiFacade.getInstance(__context__.project)
.findClass(Serializable.class.getCanonicalName()), true)
Powyższa implementacja nie jest odwzorowana 1-do-1, ale całkiem dobrze sprawdza się w większości przypadków. Zasługujący uwagi brak pola widoczny w szablonie definiujemy jako RegEx o liczbie wystąpień 0. Taki szablon możesz zaimportować w oknie wyszukiwania i dalej ulepszyć go do swoich potrzeb:
<replaceConfiguration
name="Serializable without serialVersionUID"
uuid="d5a3f346-c3cc-3aaf-9e4a-21b758444f0b"
text="class $Class$ { private static final long $Field$ = $Value$; }"
recursive="false" type="JAVA" pattern_context="default"
reformatAccordingToStyle="false" shortenFQN="false"
replacement="class $Class$ { private static final long serialVersionUID = 1L; } "
case_sensitive="true">
<constraint name="__context__" within="" contains="" />
<constraint
name="Class"
script=""!__context__.interface && !__context__.enum && !__context__.record && __context__.qualifiedName != null && __context__.isInheritor(com.intellij.psi.JavaPsiFacade.getInstance(__context__.project).findClass(Serializable.class.getCanonicalName()), true)""
target="true" within="" contains="" />
<constraint name="Field" regexp="serialVersionUID" minCount="0"
maxCount="0" within="" contains="" />
<constraint name="Value" within="" contains="" />
</replaceConfiguration>
Uwaga: Zastąpienie strukturalne powoduje również przeformatowanie kodu w całym pliku. Zachowanie to jest obecnie zgłoszone jako błąd. Warto pamiętać o tym przy podmianie starszego, niesformatowanego kodu.