通过reflection或其他方式覆盖java最终方法?

在尝试编写测试用例时会出现此问题。 Foo是框架库中的一个类,我没有源代码访问权限。

public class Foo{ public final Object getX(){ ... } } 

我的申请会

 public class Bar extends Foo{ public int process(){ Object value = getX(); ... } } 

由于其他依赖性,我无法创建Foo对象,因此unit testing用例无法初始化。 当值为null时,BarTest抛出空指针。

 public class BarTest extends TestCase{ public testProcess(){ Bar bar = new Bar(); int result = bar.process(); ... } } 

有没有办法可以使用reflectionapi将getX()设置为非final? 或者我该如何进行测试?

您可以创建另一个可以在测试中覆盖的方法:

 public class Bar extends Foo { protected Object doGetX() { return getX(); } public int process(){ Object value = doGetX(); ... } } 

然后,您可以在BarTest中覆盖doGetX。

因为这是google中“覆盖最终方法java”的最佳结果之一。 我以为我会离开我的解决方案。 这个类使用示例“Bagel”类和一个免费使用的javassist库来显示一个简单的解决方案:

 //This class shows how you can override a final method of a super class using the Javassist's bytecode toolkit //The library can be found here: http://jboss-javassist.github.io/javassist/ // //The basic idea is that you get the super class and reset the modifiers so the modifiers of the method don't include final. //Then you add in a new method to the sub class which overrides the now non final method of the super class. // //The only "catch" is you have to do the class manipulation before any calls to the class happen in your code. So put the //manipulation as early in your code as you can otherwise you will get exceptions. package packagename; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.CtNewMethod; import javassist.Modifier; public class TestCt { public static void main(String[] args) { try { // get the super class CtClass bagel = ClassPool.getDefault().get("packagename.TestCt$Bagel"); // get the method you want to override CtMethod originalMethod = bagel.getDeclaredMethod("getDescription"); // set the modifier. This will remove the 'final' modifier from the method. // If for whatever reason you needed more than one modifier just add them together originalMethod.setModifiers(Modifier.PUBLIC); // save the changes to the super class bagel.toClass(); // get the subclass CtClass bagelsolver = ClassPool.getDefault().get("packagename.TestCt$BagelWithOptions"); // create the method that will override the super class's method CtMethod overrideMethod = CtNewMethod.make("public String getDescription() { return super.getDescription() + \" with \" + getOptions(); }", bagelsolver); // add the new method to the sub class bagelsolver.addMethod(overrideMethod); // save the changes to the sub class bagelsolver.toClass(); } catch (Exception e) { e.printStackTrace(); } BagelWithOptions myBagel = new BagelWithOptions(); myBagel.setOptions("cheese, bacon and eggs"); System.out.println("My bagel is: " + myBagel.getDescription()); //The output would be: //**My bagel is: a plain bagel with cheese, bacon and eggs** } public static class Bagel { public final String getDescription() { return "a plain bagel"; } } public static class BagelWithOptions extends Bagel { String options; public BagelWithOptions() { options = "nothing else"; } public void setOptions(String options) { this.options = options; } public String getOptions() { return options; } } } 

Seb是正确的,只是为了确保你得到你的问题的答案,没有在本机代码中做一些事情(我很确定这不会工作)或在运行时修改类的字节码,并创建类在运行时覆盖该方法,我看不到改变方法“最终性”的方法。 反思对你没有帮助。

如果您的unit testing用例由于其他依赖性而无法创建Foo,那么这可能表明您没有首先对您的单元进行测试。

unit testing意味着在相同的情况下测试生产代码将运行,因此我建议在测试中重新创建相同的生产环境。 否则,您的测试将无法完成。

如果getX()返回的变量不是final那么你可以使用在unit testing私有方法的最佳方法中解释的技术吗? 用于通过Reflection更改private变量的值。

 public class Bar extends Foo{ public int process(){ Object value = getX(); return process2(value); } public int process2(Object value){ ... } } public class BarTest extends TestCase{ public testProcess(){ Bar bar = new Bar(); Mockobj mo = new Mockobj(); int result = bar.process2(mo); ... } } 

我最终做的是上面的。 它有点难看……詹姆斯解决方案绝对比这更好……