PMD Java Rules (ver 7.13.0)- Best Practices ②

PMDバージョン: 7.13.0

CheckResultSet

公式ドキュメント: CheckResultSet

Since: PMD 4.1

Priority: Medium (3)

Description:

ResultSetのナビゲーションメソッド(nextpreviousfirstlast)の戻り値を常に確認する必要があります。戻り値がfalseの場合、適切に処理しないと例外が発生する可能性があります。

Configuration:

<rule ref="category/java/bestpractices.xml/CheckResultSet" />

Example:

// NG:next() の戻り値を確認せずに値を取得
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
String name = rs.getString("name"); // ← カーソルが適切に位置していない可能性がある

// OK:next() の戻り値を確認してから値を取得
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
if (rs.next()) {
    String name = rs.getString("name"); // ← 安全に値を取得
} else {
    // データが存在しない場合の処理
}

ConstantsInInterface

公式ドキュメント: ConstantsInInterface

Since: PMD 5.5

Priority: Medium (3)

Description:

定数を定義するためにインターフェースを使用することはJavaの設計原則に反し、「Constants Interface」アンチパターンとして知られています。
これは、継承による不必要な依存関係を生み出し、実装クラスの設計を不明瞭にします。
定数はユーティリティクラスや列挙型(enum)に定義することが推奨されます。

Property:

名前 デフォルト値 説明
ignoreIfHasMethods true インターフェースに1つでもメソッドが定義されていれば、違反とみなさないようにします。

Configuration:

<!-- プロパティ設定なし -->
<rule ref="category/java/bestpractices.xml/ConstantsInInterface" />

<!-- プロパティ設定あり -->
<rule ref="category/java/bestpractices.xml/ConstantsInInterface">
    <properties>
        <property name="ignoreIfHasMethods" value="false" />
    </properties>
</rule>

Example:

// NG: 定数だけを定義する目的でインターフェースを使用しており、継承の意図が曖昧になる
public interface Constants {
    int TIMEOUT = 1000;
}

// OK: 定数をfinalクラスで定義し、インスタンス化も防止している
public final class Constants {
    public static final int TIMEOUT = 1000;

    private Constants() {} // インスタンス化を防ぐためのprivateコンストラクタ
}

DefaultLabelNotLastInSwitch

公式ドキュメント: DefaultLabelNotLastInSwitch

Since: PMD 1.5

Priority: Medium (3)

Description:

switch文において、defaultラベルは最後に配置することで、コードの可読性と予測可能性が向上します。
defaultが先にあると可読性が下がり、意図しないfall-throughの原因になることがあります。このルールは、defaultが最後にない場合に警告を出します。

Configuration:

<rule ref="category/java/bestpractices.xml/DefaultLabelNotLastInSwitch" />

Example:

// NG:default が途中にある
switch (value) {
    case 1:
        // 処理
        break;
    default:
        // 処理
        break;
    case 2:
        // 処理
        break;
}

// OK:default を最後に配置
switch (value) {
    case 1:
        // 処理
        break;
    case 2:
        // 処理
        break;
    default:
        // 処理
        break;
}

DefaultLabelNotLastInSwitchStmt

公式ドキュメント: DefaultLabelNotLastInSwitchStmt

Since: PMD 1.5

Priority: Medium (3)

Description:

switch文において、defaultラベルは通常最後に書くのが慣例であり、可読性向上や誤解の防止につながります。このルールは、defaultが最後でない場合に警告を出します。

⚠️ Deprecated(非推奨):

このルールはPMD 7.7.0で名前が変更されました。ルールの機能自体は維持されており、名称が "DefaultLabelNotLastInSwitchStmt" から "DefaultLabelNotLastInSwitch" に変わっています。
そのため、この名前は古いものとして扱われ、新しい名前での利用が推奨されます。

Configuration:

<rule ref="category/java/bestpractices.xml/DefaultLabelNotLastInSwitchStmt" />

Example:

// NG: defaultがcaseより前にあり、fall-throughの意図が不明瞭
switch (value) {
    default:
        System.out.println("default");
        break;
    case 1:
        System.out.println("one");
        break;
}

// OK: defaultが最後に配置されている
switch (value) {
    case 1:
        System.out.println("one");
        break;
    default:
        System.out.println("default");
        break;
}

DoubleBraceInitialization

公式ドキュメント: DoubleBraceInitialization

Since: PMD 6.16.0

Priority: Medium (3)

Description:

ダブルブレースイニシャライゼーション(二重括弧での初期化)は一見便利に見えますが、匿名クラスの生成を伴い、不必要なメモリ使用や外部クラスへの不適切な参照保持につながる恐れがあります。このパターンは避けるべきです。

Configuration:

<rule ref="category/java/bestpractices.xml/DoubleBraceInitialization" />

Example:

// NG: ダブルブレースイニシャライゼーション(二重括弧での初期化)により匿名クラスが生成され、意図しない参照やメモリリークの原因になる
Set<String> s = new HashSet<String>() {{
    add("one");
    add("two");
}};

// OK: 通常の初期化構文を使用し、安全で明確
Set<String> s = new HashSet<>();
s.add("one");
s.add("two");

ExhaustiveSwitchHasDefault

公式ドキュメント: ExhaustiveSwitchHasDefault

Since: PMD 7.10.0

Priority: Medium (3)

Description:

列挙型enumのすべての値を網羅しているswitch文にdefaultラベルが含まれている場合、それは将来的なenumの変更を見逃す原因になります。
defaultがあると、追加されたenum定数に対する警告が出なくなり、保守性が下がります。

Configuration:

<rule ref="category/java/bestpractices.xml/ExhaustiveSwitchHasDefault" />

Example:

enum Color { RED, GREEN, BLUE }

// NG: enumのすべての値をcaseで処理しているにもかかわらず、defaultを含めてしまっている
switch (color) {
    case RED:
        break;
    case GREEN:
        break;
    case BLUE:
        break;
    default: // これにより、新たなenum値追加時に警告が出なくなる
        break;
}

// OK: defaultがないため、enumの変更時に未処理のcaseを検出しやすい
switch (color) {
    case RED:
        break;
    case GREEN:
        break;
    case BLUE:
        break;
}

ForLoopCanBeForeach

公式ドキュメント: ForLoopCanBeForeach

Since: PMD 6.0.0

Priority: Medium (3)

Minimum Language Version: Java 1.5

Description:

forループでインデックスを使ってコレクションをループしている場合、foreach(拡張for文)を使うことでコードが簡潔で可読性が高くなります。
このルールは、forループをforeachに置き換えられる箇所を警告します。

Configuration:

<rule ref="category/java/bestpractices.xml/ForLoopCanBeForeach" />

Example:

// NG: インデックスを使った通常のforループで記述しているが、foreachにできる
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

// OK: 拡張for文を使うことでコードが簡潔になり、可読性が向上
for (String item : list) {
    System.out.println(item);
}

ForLoopVariableCount

公式ドキュメント: ForLoopVariableCount

Since: PMD 6.11.0

Priority: Medium (3)

Description:

forループで複数の変数を同時に操作すると、コードの複雑性が上がり、バグを生みやすくなります。
このルールは、1つのforループで操作される変数の数が多すぎる場合に警告を出します。

Property:

名前 デフォルト値 説明
maximumVariables 1 通常のforステートメントで許可される制御変数の最大数

Configuration:

<!-- プロパティ設定なし -->
<rule ref="category/java/bestpractices.xml/ForLoopVariableCount" />

<!-- プロパティ設定あり -->
<rule ref="category/java/bestpractices.xml/ForLoopVariableCount">
    <properties>
        <property name="maximumVariables" value="2" />
    </properties>
</rule>

Example:

// NG: 3つのループ変数を同時に操作しており、可読性や保守性が低い
for (int i = 0, j = 0, k = 0; i < n; i++, j++, k++) {
    // 複雑なループ処理
}

// OK: 単一の制御変数のみを使用し、ループがシンプルで読みやすい
for (int i = 0; i < n; i++) {
    // シンプルなループ処理
}

GuardLogStatement

公式ドキュメント: GuardLogStatement

Since: PMD 5.1.0

Priority: Medium High (2)

Description:

ログ出力の前にログレベルをチェックしないと、ログ出力処理のための文字列連結などの計算が常に実行され、パフォーマンスに悪影響を与える可能性があります。
このルールは、ログレベルのガードチェックがないログ呼び出しを検出します。

Property:

名前 デフォルト値 説明
logLevels trace, debug, info, warn, error, log, finest, finer, fine, info, warning, severe ガード対象となるログレベルの一覧
guardsMethods isTraceEnabled, isDebugEnabled, isInfoEnabled, isWarnEnabled, isErrorEnabled, isLoggable ログレベルチェックに使用されるメソッド

Configuration:

<!-- プロパティ設定なし -->
<rule ref="category/java/bestpractices.xml/GuardLogStatement" />

<!-- プロパティ設定あり -->
<rule ref="category/java/bestpractices.xml/GuardLogStatement">
    <properties>
        <property name="logLevels" value="debug,info" />
        <property name="guardsMethods" value="isDebugEnabled,isInfoEnabled" />
    </properties>
</rule>

Example:

// NG: ログレベルをチェックせずに文字列連結が行われ、パフォーマンスに悪影響を与える
logger.debug("User " + user.getName() + " logged in");

// OK: ログレベルをチェックし、無駄な処理を防止している
if (logger.isDebugEnabled()) {
    logger.debug("User " + user.getName() + " logged in");
}

ImplicitFunctionalInterface

公式ドキュメント: ImplicitFunctionalInterface

Since: PMD 7.12.0

Priority: Medium High (2)

Description:

関数型インタフェース(抽象メソッドが1つだけ定義されているインタフェース)は、明示的に@FunctionalInterfaceアノテーションを付けるべきです。
これにより、開発者が意図を明確に示すことができ、誤って抽象メソッドを追加することによる問題も防げます。

Configuration:

<rule ref="category/java/bestpractices.xml/ImplicitFunctionalInterface" />

Example:

// NG: 抽象メソッドが1つあるが、@FunctionalInterface が明示されていない
public interface Converter<T, R> {
    R convert(T t); // 関数型として利用できるが、明示されていない
}

// OK: @FunctionalInterface があり、意図が明示されている
@FunctionalInterface
public interface Converter<T, R> {
    R convert(T t); // 意図が明確で、保守性も高い
}

JUnit4SuitesShouldUseSuiteAnnotation

公式ドキュメント: JUnit4SuitesShouldUseSuiteAnnotation

Since: PMD 4.0

Priority: Medium (3)

Description:

JUnit 4では、テストスイートを作成する際には@RunWith(Suite.class)@Suite.SuiteClasses({...})アノテーションを使用すべきです。
手動でのスイートクラス実装は冗長であり、保守が難しくなります。

※「JUnit4~」のルールはJUnit3→JUnit4でアノテーションベースになったことに対するチェック

Configuration:

<rule ref="category/java/bestpractices.xml/JUnit4SuitesShouldUseSuiteAnnotation" />

Example:

// NG: JUnitスイートを手動で定義しており、冗長でエラーの元になる
public class AllTests {
    public static junit.framework.Test suite() {
        TestSuite suite = new TestSuite();
        suite.addTestSuite(MyTest.class);
        return suite;
    }
}

// OK: アノテーションを使用してスイートを定義
@RunWith(Suite.class)
@Suite.SuiteClasses({ MyTest.class })
public class AllTests {
    // クラス本体は不要
}

JUnit4TestShouldUseAfterAnnotation

公式ドキュメント: JUnit4TestShouldUseAfterAnnotation

Since: PMD PMD 4.0

Priority: Medium (3)

Description:

JUnit 4では、テスト後のクリーンアップ処理はtearDown()メソッドをオーバーライドするのではなく、@Afterアノテーションを使用するべきです。
これはJUnit 4の推奨スタイルであり、明示的であると同時に可読性も高まります。

⚠️ Deprecated(非推奨):

このルールは PMD 7.0.0 で UnitTestShouldUseAfterAnnotation に名称変更されました。今後は新しい名前のルールを使用してください。

Configuration:

<rule ref="category/java/bestpractices.xml/JUnit4TestShouldUseAfterAnnotation" />

Example:

// NG: JUnit3スタイルの tearDown() を使用している
public class MyTest extends TestCase {
    protected void tearDown() {
        // クリーンアップ処理
    }
}

// OK: JUnit4の @After アノテーションを使用
public class MyTest {
    @After
    public void cleanUp() {
        // クリーンアップ処理
    }
}

PMD 7.0のルールについて、以下の記事も書いています。

HTML Rule:

olafnosuke.hatenablog.com

JavaScript Rule - Best Practices:

olafnosuke.hatenablog.com

JavaScript Rule - Code Style:

olafnosuke.hatenablog.com