java-coding-guideline について
java-coding-guidelineは、Java開発者向けのコーディング規約スキルです。スタイル、null処理、リソース管理に加え、外部境界と内部不変条件で例外とアサーションを使い分けます。
主な機能
- 01明示的インポートと空行の規則
- 02null許容アノテーションと型の選択
- 03コンストラクター、不変性、コンポジションの指針
- 04リソース管理と例外捕捉の規則
- 05信頼境界に応じた例外とアサーション
活用シーン
- 01Javaコードレビュー基準の統一
- 02null処理とリソース管理の実装
- 03入力検証で例外かアサーションかを判断
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ gabia/ agent-skills --skill java-coding-guidelineインストール後、Claude Code で/java-coding-guidelineを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/async.mdjavadoc.mdlombok.mdsecurity-resources.md詳細
Java コーディングガイドライン
重大:より具体的なスキルによって明示的に上書きされない限り、これらのルールはすべての Java コードに適用されます。
インポート文
コードの明瞭さと保守性を高めるため、ワイルドカードを使わず、常に明示的にインポートしてください。
✅ 推奨
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
❌ 非推奨
import static org.assertj.core.api.Assertions.*;
縦方向の間隔(読みやすさ)
クラス内の縦方向の間隔を統一し、差分を小さくするとともに、コードを見やすくしてください。
ルール
- 次の箇所の間には、空行をちょうど1行入れてください。
- フィールドのまとまりと次のフィールドのまとまり(グループ化されている場合)
- フィールドと最初のコンストラクター
- コンストラクターとメソッド
- メソッドと次のメソッド
- また、クラスの開始
{の直後(最初のフィールドまたは初期化子の前)にも空行を1行入れてください。ただし、クラスが意図的に空の場合は除きます。
✅ 推奨
public final class SomeClass {
@NonNull
private final String fieldA;
@NonNull
private final String fieldB;
public void someFunc() {
// some action
}
public String getFieldA() {
return fieldA;
}
}
❌ 非推奨
public final class SomeClass {
@NonNull
private final String fieldA;
@NonNull
private final String fieldB;
public void someFunc() {
// some action
}
public String getFieldA() {
return fieldA;
}
}
コンストラクター
プライマリーコンストラクターを1つだけ使用し、補助コンストラクターからは this() を使って委譲してください。
✅ 推奨
public final class SomeClass {
private final int field1;
private final int field2;
public SomeClass() {
this(1, 2);
}
public SomeClass(int field1, int field2) {
this.field1 = field1;
this.field2 = field2;
}
}
❌ 非推奨
public final class SomeClass {
private final int field1;
private final int field2;
public SomeClass() {
this.field1 = 1;
this.field2 = 2;
}
public SomeClass(int field1, int field2) {
this.field1 = field1;
this.field2 = field2;
}
}
Optional の使用
戻り値やパラメーターとして Optional を使用するのは避け、代わりに @Nullable アノテーションを使用してください。
✅ 推奨
public final class SomeClass {
@Nullable
public Integer someNullable() {
if (something) return null;
return 1;
}
public void func(@Nullable Integer param) {
// do something
}
}
❌ 非推奨
public final class SomeClass {
@NonNull
public Optional<Integer> someNullable() {
if (something) return Optional.ofNullable(null);
return Optional.ofNullable(1);
}
public void func(@NonNull Optional<Integer> param) {
// do something
}
}
ラッパー型とプリミティブ型
nullが想定される場合はラッパー型を使用してくださいnullが発生すべきでない場合はプリミティブ型を使用してください
✅ 推奨
public final class SomeClass {
private final int field1;
@Nullable
private final Long field2;
public SomeClass(int field1, @Nullable Long field2) {
this.field1 = field1;
this.field2 = field2;
}
}
❌ 非推奨
public final class SomeClass {
@NonNull
private final Integer field1;
@Nullable
private final Long field2;
public SomeClass(@NonNull Integer field1, @Nullable Long field2) {
this.field1 = field1;
this.field2 = field2;
}
}
null の比較
- 一般的な場合は
A == nullを使用してください - Stream API または関数型プログラミングのコンテキストでは
Objects.isNullを使用してください
✅ 推奨
final Integer someVariable = null;
if (someVariable == null) {
// do something
}
List<Integer> lists = new ArrayList<>();
lists.stream()
.filter(Objects::isNull)
.collect(Collectors.toList());
❌ 非推奨
final Integer someVariable = null;
if (Objects.isNull(someVariable)) {
// do something
}
List<Integer> lists = new ArrayList<>();
lists.stream()
.filter(p -> p == null)
.collect(Collectors.toList());
Nullability アノテーション
- JSpecify アノテーションを使用してください
- private メソッドも含め、すべてのメソッドに
@Nullableと@NonNullを適用してください - クラスレベルまたはパッケージレベルのアノテーションは避けてください(Lombok との互換性に問題があるため)
✅ 推奨
public final class SomeClass {
@Nullable
private final Integer field1;
@NonNull
private final String field2;
@NonNull
public String func() {
return process();
}
@Nullable
private String process() {
if (field1 == null) return null;
return "awesome";
}
}
❌ 非推奨
public final class SomeClass {
private final Integer field1;
private final String field2;
public String func() {
return process();
}
private String process() {
if (field1 == null) return null;
return "awesome";
}
}
パッケージ名
Google Java Style Guide の規則に従い、小文字と数字のみを使用してください。
✅ 推奨
package com.example.project.initscript;
❌ 非推奨
package com.example.project.initScript;
静的ファクトリーメソッド
of の命名規則のみを使用してください。複数の型がある場合は、of<Postfix> の形式を使用してください。
✅ 推奨
public final class SomeClass {
private final int field1;
private SomeClass(int field1) {
this.field1 = field1;
}
public static SomeClass ofDefault() {
return new SomeClass(1);
}
public static SomeClass ofSpecial() {
return new SomeClass(2);
}
}
❌ 非推奨
public final class SomeClass {
private final int field1;
private SomeClass(int field1) {
this.field1 = field1;
}
public static SomeClass from() {
return new SomeClass(1);
}
public static SomeClass valueOf() {
return new SomeClass(2);
}
}
リソース管理
AutoCloseable リソースには必ず try-with-resources を使用してください。ファイナライザーには決して依存しないでください。
Try-With-Resources
✅ 推奨
public String readFile(@NonNull Path path) throws IOException {
try (var reader = Files.newBufferedReader(path)) {
return reader.lines().collect(Collectors.joining("\n"));
}
}
// Multiple resources
public void copyStream(@NonNull InputStream in, @NonNull OutputStream out) throws IOException {
try (in; out) { // Java 9+: can use already-declared variables
in.transferTo(out);
}
}
❌ 非推奨
public String readFile(@NonNull Path path) throws IOException {
BufferedReader reader = Files.newBufferedReader(path);
try {
return reader.lines().collect(Collectors.joining("\n"));
} finally {
reader.close(); // The original exception may be suppressed if one occurs here
}
}
継承よりコンポジション
継承よりコンポジションを優先してください。
✅ 推奨
public final class SomeFeature {
public void func() { /* ... */ }
}
public final class SomeClass {
private final SomeFeature feature;
public void func2() {
feature.func();
// do something
}
}
❌ 非推奨
public class SomeFeature {
public void func() { /* ... */ }
}
public final class SomeClass extends SomeFeature {
public void func2() {
super.func();
// do something
}
}
不変オブジェクト
拡張やフレームワークの要件で必要な場合を除き、すべてのオブジェクトで final キーワードを使用してください。
✅ 推奨
public final class SomeClass {
// ...
}
❌ 非推奨
public class SomeClass {
// ...
}
アサーションと例外
信頼境界と、呼び出し元が復旧を担う責任に基づいて、アサーションと例外を使い分けてください。このルールはクラスやメソッド全体ではなく、各条件に適用します。
Java のアクセス修飾子やコンポーネント名から境界を推測しないでください。public メソッドが内部 API の場合もあれば、private パーサーが外部入力を処理する場合もあります。
判断表
| 条件 | 対応方法 |
|---|---|
| 外部からの不正なリクエスト、コマンド、メッセージ、または公開 API の引数 | 例外をスローする |
| 実行時設定が無効 | 例外をスローする |
| ネットワーク、データベース、ファイルシステム、または外部サービスの障害 | 例外をスローする |
| セキュリティ、認可、またはデータ整合性のチェック | 例外をスローする |
| 呼び出し元が障害を変換、再試行、追跡、記録、またはその他の方法で処理する必要がある | 例外をスローする |
| 内部 API における、プログラマーが制御する事前条件または事後条件 | assert を使用する |
| 内部の配線またはディスパッチによって保証される関係 | assert を使用する |
| 実装が正しければ到達しない状態 | assert を使用する |
外部境界を越える場合は例外をスローする
有効な実行時動作中に障害が発生する可能性がある場合、または呼び出し元がその障害に対応できる必要がある場合は、例外をスローします。外部境界には次のものがあります。
- HTTP、RPC、XML-RPC、CLI、メッセージ、イベントのハンドラー
- 独立して開発された利用者が呼び出す公開ライブラリ API
- ユーザーまたはテナントから提供される入力
- 実行時設定
- 通常の動作中に発生する可能性がある、ネットワーク、データベース、ファイルシステム、外部サービスからの予期しないデータや無効なデータ
アサーションが有効な場合でも、例外を使用します。外部から見える動作を JVM の -ea オプションに依存させてはいけません。
障害の内容が伝わる例外の型を選びます。
- 呼び出し元から渡された引数が無効な場合は
IllegalArgumentExceptionを使用します - 現在の実行時状態では操作を実行できない場合は
IllegalStateExceptionを使用します - 呼び出し元が区別する必要のある運用上の障害には、ドメイン固有の例外を使用します
- I/O 障害を意図的に非チェック例外として公開する場合は
UncheckedIOExceptionを使用します
外部から見える例外は @throws でドキュメント化します。
内部不変条件をアサートする
内部 API では、プログラマーが管理する事前条件、事後条件、不変条件に assert を使用します。これには、信頼できるアプリケーションデータを処理する内部パブリッシャー、ジェネレーター、コンバーター、パーサーが含まれます。
アサーションを使用する一般的なケースは次のとおりです。
- 内部リゾルバーが空白でないキーを生成する必要がある
- ジェネレーターまたはコンバーターが空でない結果を生成する必要がある
- 関連するドメインオブジェクトが互いを参照する必要がある
- 永続化処理で ID が設定される必要がある
- 挿入直後のクエリーで、挿入したエンティティが返される必要がある
- インターセプターが内部コンテキスト値を設定している必要がある
- 内部のディスパッチが正しければ、ある分岐に到達しない必要がある
public void publish(@NonNull String objectKey, @NonNull String generatedScript) {
assert !objectKey.isBlank();
assert !generatedScript.isBlank();
objectStore.put(objectKey, generatedScript);
}
本番環境ではアサーションが無効になっている場合があります。そのため、次の点を守ります。
- 外部入力の検証にアサーションを使用しないでください
- 認可やセキュリティのチェックにアサーションを使用しないでください
- 実行時のリカバリーが必要な条件にアサーションを使用しないでください
- アサーション式に副作用を含めないでください
- 通常の制御フローとして
AssertionErrorを捕捉しないでください - アサーションが無効でも、コードの正確性とセキュリティを保ってください
不変条件が自明でない場合は、アサーションメッセージを追加します。
assert collector.getAgentId() == agent.getId() : "collector must own the agent";
条件ごとにルールを適用する
同じメソッド内で、アサーションと例外の両方を使用する場合があります。
public void resize(@NonNull Pool pool, @NonNull Bucket bucket, long requestedSize, long minimumSize) {
assert pool.getId() == bucket.getPoolId();
assert pool.getType() == PoolType.OBJECT_STORAGE;
if (requestedSize < minimumSize) {
throw new IllegalArgumentException("requested size is smaller than the minimum size");
}
}
プールの関係性は内部ルーティングの不変条件です。要求されたサイズは実行時引数であり、呼び出し元が正しくない値を渡すことは十分にあり得ます。
内部 API でも、S3 のアップロード失敗、データベースエラー、ネットワークタイムアウトなど、運用上の障害には例外をスローする必要があります。内部 API であるというだけで、想定される実行時障害がアサーションに変わるわけではありません。
一度検証してから、内部ではアサートする
外部データは境界で例外を使用して検証します。信頼できる内部データに変換した後は、伝播する不変条件を記録するためにアサーションを使用します。
パーサーにも同じルールが適用されます。
- 外部または信頼できないコンテンツを解析するときは、解析例外をスローします
- 同じアプリケーションで生成および検証されたコンテンツを解析するときは、前提条件をアサートします
例外のログ出力
例外をスローしても、すべてのレイヤーでログを出力する必要はありません。元の原因を保持し、操作を処理、変換、再試行、または終了できるレイヤーまで例外を伝播させます。各レイヤーでログを出力してから再スローするのではなく、その処理境界で一度だけ例外をログに記録します。
例外の捕捉
必要最小限の例外だけを捕捉します。
✅ 推奨
try {
// do something
} catch (IllegalArgumentException e) {
// do catch
}
❌ 非推奨
try {
// do something
} catch (RuntimeException e) {
// do catch
}
AutoCloseable の実装
AutoCloseable を実装する場合:
close()をべき等(複数回呼び出しても安全)にしますclose()から例外を決してスローしないでください(抑制された例外に関する問題があるため)- 例外をスローする代わりに、エラーをログに記録します
✅ 推奨
public final class DatabaseConnection implements AutoCloseable {
@NonNull
private final Connection connection;
private volatile boolean closed = false;
@Override
public void close() {
if (closed) return; // Idempotent: safe to call multiple times
closed = true;
try {
connection.close();
} catch (SQLException e) {
// Log only; do not throw (exceptions in close can suppress the original)
logger.warn("Failed to close connection", e);
}
}
}
❌ 非推奨
public final class DatabaseConnection implements AutoCloseable {
@NonNull
private final Connection connection;
@Override
public void close() throws SQLException {
connection.close(); // Not idempotent; exception propagates
}
}
日付/時刻 API
java.time(JSR-310)のみを使用してください。新しいコードでは java.util.Date と java.util.Calendar の使用は禁止されています。
型の選択
| 用途 | 型 | 例 |
|---|---|---|
| 保存/転送(UTC) | Instant | API のタイムスタンプ、DB への保存 |
| ユーザーへの表示 | ZonedDateTime | UI に表示する時刻 |
| 日付のみ | LocalDate | 誕生日、有効期限 |
| 時刻のみ | LocalTime | 営業時間、アラーム |
| 期間 | Duration / Period | 経過時間、日付の差 |
✅ 推奨
public final class Event {
@NonNull
private final Instant createdAt; // UTC timestamp
@NonNull
private final LocalDate eventDate; // Date only
@NonNull
private final ZoneId timeZone; // Display time zone
@NonNull
public ZonedDateTime displayTime() {
return createdAt.atZone(timeZone);
}
}
フォーマッターの再利用(スレッドセーフ):
public final class DateFormats {
// DateTimeFormatter is thread-safe, so reuse as constants
public static final DateTimeFormatter ISO_DATE = DateTimeFormatter.ISO_LOCAL_DATE;
public static final DateTimeFormatter ENGLISH_DATE = DateTimeFormatter.ofPattern("MMM dd, yyyy");
private DateFormats() {}
}
❌ 非推奨
public final class Event {
@NonNull
private final Date createdAt; // Do not use java.util.Date
@NonNull
private final Calendar calendar; // Do not use java.util.Calendar
@NonNull
public String format() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // Not thread-safe
return sdf.format(createdAt);
}
}
レガシー API との連携(境界でのみ)
Date を必要とするレガシー API と連携する場合は、境界で変換してください。
// Instant → Date (boundary layer only)
Date legacyDate = Date.from(instant);
// Date → Instant (convert immediately on receipt)
Instant instant = legacyDate.toInstant();
その他のガイドライン
ビルダーパターン
API の一貫性を保つため、Lombok 形式のビルダーを使用してください。
変数とメソッドの命名
- JVM 10 以降の
varキーワードを使用してください - 意味の分かる変数名を使用してください
- ただし、変数名、関数名、フィールド名が長くなりすぎないようにしてください
- メソッド名には動詞を使用してください
- ゲッターを除き、
get接頭辞の使用は避けてください
キャメルケースの規則
大文字と小文字が混在する単語や略語にはキャメルケースを適用してください(例:「MultiNIC」は「MultiNic」にします)。
制御フロー文
波括弧を使わず、単純な if 文には1行構文を使用してください。
✅ 推奨
if (someCondition) doSomething();
❌ 非推奨
if (someCondition) {
doSomething();
}
コメント
- 特別な場合にのみコメントを使用してください
- TODO コメントは、すぐに取り組むタスクに使用してください
- コードの内容を繰り返す不要なコメントは避けてください
関連項目
リファレンスガイド
各トピックを詳しく理解するには、以下を参照してください。
- references/async.md - CompletableFuture と非同期プログラミングのパターン
- references/javadoc.md - Javadoc の規約と例外のドキュメント化
- references/lombok.md - 使用可能および禁止されている Lombok アノテーション
- references/security-resources.md - セキュリティのベストプラクティス、パスワードの取り扱い、SQL インジェクションの防止、PII のマスキング