Page 150 - 《软件学报》2025年第7期
P. 150
孙伟杰 等: Java 依赖异味的实证研究与统一检测技术 3071
(a) 危害 1 import mod.Dup
执行 Dup classDup=new Dup ();
classDup.methodLib (); 构建错误
依赖于
模块 mod 依赖库 lib
包含类 包含类
mod.Dup mod.Dup
public class Dup{ public class Dup{
void methodHost (); void methodLib ();
} }
(b) 危害 3 import mod.RunDup
RunDup runner = new RunDup();
执行
runner.runMtdLib(); 运行时错误
依赖于 包含类 lib.RunDup
import mod.Dup;
public class RunDup{
模块 mod 依赖库 lib public void runMtdLib (){
包含类 包含类 Dup classDup=new Dup ();
mod.Dup mod.Dup classDup.methodLib ();
public class Dup{ public class Dup{ }
void methodHost (); void methodLib (); }
} }
(c) 危害 4 import mod.Dup
Dup classDup=new Dup();
执行
System.out.println (classDup.methodDup ());
依赖于
运行时语义冲突
模块 mod 依赖库 lib
包含类 包含类
mod.Dup mod.Dup
public class Dup{ public class Dup{
String methodDup (){ String methodDup (){
return “mod” return “Lib”
} }
} }
图 A3 异味 1.1 可能危害与对应触发场景
(1) 构建错误: 其触发场景为模块源码中调用了仅存在于被遮蔽的同名冲突类中的方法. 以图 A4(a) 为例, 模
块 mod 的依赖库 lib1 和 lib2 中存在冲突类 Dup, mod 试图调用仅存在于 lib2 中的方法 methodLib2, 但由于 lib1 在
依赖树中与 mod 距离更近, 最终 lib2 中的类 Dup 被忽略, 导致构建错误.
(2) 运行时错误: 其触发场景为项目运行时调用了仅存在于被遮蔽的同名冲突类中的方法. 以图 A4(b) 为例,
模块 mod 的依赖库 lib1 和 lib2 中存在冲突类 Dup, mod 试图调用 lib2 中的方法 runMtdLib, 其间接调用了 Dup 中
的方法 methodLib2. 由于并未直接调用 methodLib2, 模块可以正常进行构建. 但在运行时, JVM 会忽略 lib2 中的
类 Dup, 因此在运行时则无法找到 lib 对应方法, 导致运行时错误.
(3) 运行时语义冲突: 其触发场景为模块运行时调用了冲突类中签名相同但实现不同的方法. 以图 A4(c) 为例,
模块 mod 的依赖库 lib1 和 lib2 中存在冲突类 Dup 和同签名方法 methodLib, 但其具体实现不同. 当 mod 调用
methodLib 时就会出现语义冲突, 预期调用的是 lib2 中的 Dup 类, 对应的 methodDup 返回“lib2”, 而实际调用的是
lib1 中的 Dup 类, 对应的 methodDup 返回“lib1”.
3) 类别 1.3 库版本冲突
库版本冲突可能引发一系列严重的后果, 包括但不限于模块构建错误、运行时错误以及运行时语义冲突. 以
下将详细阐述这些危害的具体触发场景.
(1) 构建错误: 其触发场景为模块源码中调用了仅存在于被遮蔽的库中的特性 (例如方法). 以图 A5(a) 为例,
模块 mod 的依赖树中同时存在 lib1 的 1.0.0 和 2.0.0 版本. 根据 Maven 和 Gradle 的版本冲突解决机制, 最终 1.0.0

