Java 移除了 C 的「條件編譯」(#ifdef),但透過**套件匯入機制**可實現同樣效果:在不修改業務代碼的前提下,透過切換 import 套件,替換整組實作類別。本文將展示如何用此技巧實現「斷言工具」的開發/發布版本切換,並說明其核心原理與應用場景。
#ifdef 處理跨平台差異 → Java 設計為「一次編寫、到處執行」,原生跨平台無需條件編譯建立兩個同名類別 Assert,放在不同套件:
package com.bruceeckel.tools.debug;
public class Assert {
private static void perr(String msg) { System.err.println(msg); }
public final static void is_true(boolean exp) {
if (!exp) perr("Assertion failed");
}
public final static void is_false(boolean exp) {
if (exp) perr("Assertion failed");
}
public final static void is_true(boolean exp, String msg) {
if (!exp) perr("Assertion failed: " + msg);
}
public final static void is_false(boolean exp, String msg) {
if (exp) perr("Assertion failed: " + msg);
}
}
package com.bruceeckel.tools;
public class Assert {
public final static void is_true(boolean exp) {}
public final static void is_false(boolean exp) {}
public final static void is_true(boolean exp, String msg) {}
public final static void is_false(boolean exp, String msg) {}
}
發布版方法體為空,**編譯器內聯優化後完全消除呼叫**,零執行期開銷。
package c05;
// 開發期:import com.bruceeckel.tools.debug.*;
// 發布期:import com.bruceeckel.tools.*;
import com.bruceeckel.tools.debug.*;
public class TestAssert {
public static void main(String[] args) {
Assert.is_true((2 + 2) == 5); // 開發期印錯誤,發布期無操作
Assert.is_false((1 + 1) == 2); // 同上
Assert.is_true((2 + 2) == 5, "2+2=5");
Assert.is_false((1 + 1) == 2, "1+1!=2");
}
}
import 解析 Assert 類別debug 套件 → 呼叫含檢查邏輯的版本tools 套件 → 呼叫空方法版本TestAssert **完全不修改**| 維度 | C #ifdef | Java 匯入切換 |
|---|---|---|
| 語法侵入性 | 高(原始碼散佈 #ifdef) | 零(業務碼無條件編譯痕跡) |
| 類型安全 | 低(前處理器無類型檢查) | 高(正常編譯期類型檢查) |
| IDE 支援 | 差(灰色代碼、跳轉失效) | 完整(正常導航、重構、檢查) |
| 封裝性 | 差(實作細節洩露在業務碼) | 好(實作完全封裝在套件內) |
此技巧適用於任何「需在不修改業務碼下切換實作」的場景:
DebugLogger、發布期 NullLoggerH2Driver、生產用 PostgresDriverMockPaymentService、生產用 RealPaymentServiceDetailedProfiler、發布期 NoOpProfilerinterface Assert { ... },兩類別 implements Assert,編譯器強制一致性import 或依賴範圍Java 雖無條件編譯,但透過**套件命名空間隔離 + 匯入切換**,實現了更優雅、類型安全、IDE 友善的實作替換機制。這正是「介面導向設計」在建構層面的自然延伸:業務碼依賴介面,實作由匯入決定,編譯期綁定,零執行期開銷。