---
name: java-coding-standards
description: 針對 Spring Boot 服務的 Java 編碼標準：包含命名規範、不可變性、Optional 使用、串流 (Streams)、例外處理、泛型以及專案結構。
---

# Java 編碼標準 (Java Coding Standards)

適用於 Spring Boot 服務中可讀且易於維護的 Java (17+) 程式碼標準。

## 何時啟用

- 在 Spring Boot 專案中編寫或審查 Java 程式碼。
- 執行有關命名、不可變性或例外處理的慣例。
- 使用 Records、Sealed Classes 或模式匹配 (Pattern Matching) (Java 17+)。
- 審查 Optional、串流或泛型的使用。
- 規劃套件結構與專案佈局。

## 核心原則

- **清晰重於取巧**：程式碼應易於理解。
- **預設不可變**：盡可能減少共享的可變狀態。
- **儘早報錯 (Fail Fast)**：拋出具備明確語義的例外。
- **一致性**：保持命名與套件結構的一致性。

## 命名規範

```java
// ✅ 類別 (Classes) / Records：PascalCase (大駝峰)
public class MarketService {}
public record Money(BigDecimal amount, Currency currency) {}

// ✅ 方法 / 欄位：camelCase (小駝峰)
private final MarketRepository marketRepository;
public Market findBySlug(String slug) {}

// ✅ 常數：UPPER_SNAKE_CASE (全大寫底線)
private static final int MAX_PAGE_SIZE = 100;
```

## 不可變性 (Immutability)

```java
// ✅ 優先使用 Records 與 final 欄位
public record MarketDto(Long id, String name, MarketStatus status) {}

public class Market {
  private final Long id;
  private final String name;
  // 僅提供 Getters，不提供 Setters
}
```

## Optional 的使用

```java
// ✅ find* 方法應回傳 Optional
Optional<Market> market = marketRepository.findBySlug(slug);

// ✅ 使用 map/flatMap 而非直接 get()
return market
    .map(MarketResponse::from)
    .orElseThrow(() -> new EntityNotFoundException("找不到指定的市場資料"));
```

## 串流 (Streams) 最佳實踐

```java
// ✅ 使用串流進行轉換，保持細節簡潔，避免過長的鏈結
List<String> names = markets.stream()
    .map(Market::name)
    .filter(Objects::nonNull)
    .toList();

// ❌ 避免過於複雜的嵌套串流；為了清晰起見，迴圈有時是更好的選擇
```

## 例外處理 (Exceptions)

- 針對領域錯誤使用未檢查例外 (Unchecked Exceptions)；將技術性例外封裝並補足上下文。
- 建立領域特定的例外（例如：`MarketNotFoundException`）。
- 避免使用廣泛的 `catch (Exception ex)`，除非是為了在中心位置重新拋出或記錄。

```java
throw new MarketNotFoundException(slug);
```

## 泛型與型別安全

- 避免使用原始型別 (Raw Types)；務必宣告泛型參數。
- 在開發可重用的工具型函式時，優先考慮有界泛型 (Bounded Generics)。

```java
public <T extends Identifiable> Map<Long, T> indexById(Collection<T> items) { ... }
```

## 專案結構 (Maven/Gradle)

```
src/main/java/com/example/app/
  config/       # 配置類別
  controller/   # REST API 控制器
  service/      # 業務邏輯服務
  repository/   # 資料存取層
  domain/       # 實體與領域模型
  dto/          # 資料傳輸物件
  util/         # 工具類別
src/main/resources/
  application.yml
src/test/java/... (與 main 目錄結構映射)
```

## 格式與風格

- 專案內必須一致使用 2 個或 4 個空格進行縮排。
- 每個檔案僅定義一個公開的頂層型別。
- 保持方法短小且聚焦於單一功能；適時提取輔助方法 (Helpers)。
- 成員排序：常數、欄位、建構子、公開方法、受保護方法、私有方法。

## 應避免的程式碼壞味道 (Code Smells)

- **過長的參數列表**：應改用 DTO 或建構者模式 (Builder)。
- **深層嵌套**：優先使用提早回傳 (Early Returns)。
- **魔術數字**：應改用具名的常數。
- **靜態可變狀態**：優先考慮使用依賴注入 (Dependency Injection)。
- **安靜的 Catch 塊**：必須記錄日誌並採取行動，或重新拋出。

## 日誌紀錄 (Logging)

```java
private static final Logger log = LoggerFactory.getLogger(MarketService.class);
log.info("擷取市場資料 slug={}", slug);
log.error("擷取市場資料失敗 slug={}", slug, ex);
```

## 空值 (Null) 處理

- 僅在不可避免時接受 `@Nullable`；否則應預設使用 `@NonNull`。
- 在輸入端使用 Bean Validation（如 `@NotNull`, `@NotBlank`）。

## 測試期望

- 使用 JUnit 5 與 AssertJ 進行流暢的斷言 (Fluent Assertions)。
- 使用 Mockito 進行 Mock；盡可能避免使用部分 Mock (Partial Mocks)。
- 追求確定性的測試；不要在測試中使用隱含的休眠 (Sleep)。

**請記住**：保持程式碼具備意圖性、強型別且可觀察。在證明有必要進行微觀技術優化之前，應優先優化維護性。
