Eclipse Modeling Framework (EMF) 是模型驅動開發的核心框架,其反射 API 提供了強大的運行時模型操作能力。本文詳細闡述如何利用 EMF 反射機制實現模型對象的深度比較,並輸出差異列表供版本控制、模型同步等場景使用。
EMF 可從 XML Schema、UML 或帶註解的 Java 介面生成代碼,支援圖形化編輯、序列化等功能。Ecore 元模型是 EMF 的核心,定義了模型結構並提供運行時支援,包含修改通知、XMI 序列化及通用反射 API。

關鍵類型說明:
java.lang.Object,方法以「e」開頭(如 eClass()、eGet())。EAttribute) 與引用 (EReference)。EDataType 指定。EAttribute 與 EReference 的共同父類,可視為統一的「字段」抽象。eINSTANCE 靜態實例。
核心反射方法:
eGet(EStructuralFeature):獲取屬性值,等同於 eGet(feature, true)(會解析代理)。eGet(EStructuralFeature, boolean):第二參數控制是否載入代理對象。eSet(EStructuralFeature, Object):設定屬性新值。eIsSet(EStructuralFeature):檢查屬性是否已設值。eUnset(EStructuralFeature):重置屬性值。所有 EMF 對象皆實作 EObject,故均可使用上述 API,這是實現通用比較器的基石。
透過 EPackage.eINSTANCE.getXXX_YYY() 取得 EStructuralFeature,再呼叫 eGet() 即可取值,無需針對具體類型轉型,大幅降低耦合與維護成本。
EStructuralFeature feature = ModelPackage.eINSTANCE.getNodeElement_Description();
Object remoteValue = remote.eGet(feature);
Object localValue = local.eGet(feature);

EAttribute 對應簡單類型,EReference 對應組合類型(EClass)。程式碼判斷邏輯:
if (remote.eGet(feature) instanceof EList || local.eGet(feature) instanceof EList) {
// 集合類型,需遍歷元素比較
} else if (remote.eGet(feature) instanceof EObject || local.eGet(feature) instanceof EObject) {
// 組合類型,遞歸比較
} else {
// 簡單類型
}
利用 Java 自動裝箱特性,統一用 Object.equals() 比較內容,避免 == 比較引用的問題:
Object remoteValue = remote.eGet(feature);
Object localValue = local.eGet(feature);
if (remoteValue != null && localValue != null) {
if (!remoteValue.equals(localValue)) {
// 記錄差異
}
} else if ((remoteValue == null) != (localValue == null)) {
// 一方為 null,記錄差異
}
組合類型需遞歸分解至簡單類型,並以備忘錄 (_comparedNodes) 防止循環引用導致無限遞歸。列表 (EList) 比較策略:
private void compare(List remote, List local, int compareType, List<CompareInfo> compareLog) {
if (local == null || local.isEmpty()) { /* 全記為遠端新增 */ }
for (EObject remoteObject : remote) {
EObject localObject = findElementByID(local, remoteObject);
if (localObject != null) {
compareT(remoteObject, localObject, compareType, compareLog);
} else {
compareLog.add(new CompareInfo(CompareInfo.REMOTE_ADD, ...));
}
}
for (EObject localObject : local) {
if (findElementByID(remote, localObject) == null) {
compareLog.add(new CompareInfo(CompareInfo.REMOTE_DELETE, ...));
}
}
}

ModelComparePort 整合上述邏輯,適用於本地/遠端模型同步場景:
initALLStructuralFeature()):控制比較範圍,避免無關字段干擾。isSuitable()):遞歸檢查 EStructuralFeature 是否屬於對象類別或其超類。CompareInfo 列表。public void compareT(EObject remote, EObject local, int compareType, List<CompareInfo> compareLog) {
// 空值、刪除狀態、備忘錄檢查...
for (EStructuralFeature feature : _featureList) {
if (isSuitable(remote, feature)) {
// 依 instanceof EList / EObject / 簡單類型 分派比較邏輯
}
}
}

樹視圖以紅色 X 表刪除、綠色 + 表新增、筆圖示表修改,右下角標示操作來源(本地/遠端)。
後記:若業務需求涉及複雜比較規則(如視空字串與 null 為相同),可引入「字段比較器」策略模式,將特定字段比較邏輯委派給專屬比較器,提升靈活性。