将Java静态方法包装为非静态对象以支持Mock的最简洁实现方式
方案汇总
分Kotlin、Java两类场景提供最简实现:
Kotlin 场景(推荐优先用Kotlin实现包装,代码量最少)
方案1:接口默认实现(无冗余实现类,100%覆盖需求)
直接把转发逻辑写在接口默认方法中,无需额外编写实现类:
// 仅需定义一个接口即可,无需单独写Wrapper实现类 interface FooWrapper { // 每个方法一行代码转发到静态类 fun bar() = Foo.bar() fun baz(input: String): Int = Foo.baz(input) // 内置默认单例实现,业务侧直接用 companion object : FooWrapper }
业务侧使用方式:
// 业务类构造器声明依赖,默认使用内置实现 class BusinessService( private val fooWrapper: FooWrapper = FooWrapper ) { fun test() { // 原有Foo.bar()替换为下面的写法即可 fooWrapper.bar() } }
单元测试直接mockFooWrapper接口即可,完全符合要求。如果静态方法少,甚至可以不用定义接口,直接把方法作为函数类型参数传入业务类,比如private val bar: () -> Unit = Foo::bar,连接口都省了。
方案2:KSP注解自动生成(适合静态类数量多的场景)
如果有数十个以上的静态类需要包装,可以写个简单的KSP注解处理器,自定义@GenerateStaticWrapper注解,给静态类加注解后自动生成上述接口代码,完全不需要手动写任何包装逻辑。
Java 场景
方案1:接口默认实现(和Kotlin思路一致,Java 8+支持)
同样不需要单独写实现类:
public interface FooWrapper { default void bar() { Foo.bar(); } default int baz(String input) { return Foo.baz(input); } // 内置默认单例 FooWrapper INSTANCE = new FooWrapper() {}; }
业务侧使用:
public class BusinessService { private final FooWrapper fooWrapper; // 构造器默认使用内置实现 public BusinessService() { this(FooWrapper.INSTANCE); } // 测试用构造器注入mock public BusinessService(FooWrapper fooWrapper) { this.fooWrapper = fooWrapper; } public void test() { fooWrapper.bar(); } }
方案2:Lombok简化(如果项目已经引入Lombok)
用Lombok的@Delegate注解进一步减少模板代码:
public class FooWrapper { public interface FooApi { void bar(); int baz(String input); } @Delegate(types = FooApi.class) private static final FooApi IMPL = new FooApi() { @Override public void bar() { Foo.bar(); } @Override public int baz(String input) { return Foo.baz(input); } }; }
方案3:动态代理(适合超大量静态类场景)
如果静态类数量极多,又不想一个个写接口,可以基于JDK动态代理或者CGLIB写个通用的代理生成工具,传入静态类的Class对象自动生成转发逻辑,不过这类方案运行时有轻微性能损耗,小项目不推荐。
额外优化建议
替换原有静态调用的时候,可以用IDE的全局替换功能,批量把Foo.替换为fooWrapper.,几分钟就能完成全量替换,不需要手动改每一处。
内容的提问来源于stack exchange,提问作者MyStackRunnethOver
相关产品推荐
相关产品推荐

