使用反射设置私有字段适用于静态或最终版本,但不适用于静态最终版本(组合)

凯文·克鲁伊森

我的Android项目有两个UnitTest项目。一种用于JUnit测试,另一种用于Android单元测试。在JUnit测试项目中,我创建了一个用于访问或设置私有字段,方法或构造函数的类。(PS:对于那些对完整代码感到好奇的人,请告诉我,我将其添加到这篇文章的底部。)

我也有UnitTests来测试这些私有方法访问。现在,所有这些UnitTests都可以工作,请接受以下一项:设置最终静态字段的值。

这是我用来设置私有字段的方法:

// Test method to set a private Field from a class
public static void setPrivateField(Object ob, String fieldName, Object value) throws MyUnitTestException{
    try {
        Field field = ob.getClass().getDeclaredField(fieldName);
        if(field != null){
            field.setAccessible(true);
            if(Modifier.isFinal(field.getModifiers())){
                Field modifierField = Field.class.getDeclaredField("modifiers");
                modifierField.setAccessible(true);
                modifierField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

                /*int modifiers = field.getModifiers();
                Field modifierField = field.getClass().getDeclaredField("modifiers");
                modifiers = modifiers & ~Modifier.FINAL;
                modifierField.setAccessible(true);
                modifierField.setInt(field, modifiers);*/
            }
            // ** IllegalAccessException at the following line with final static fields:
            field.set(ob, value);  // static fields ignore the given Object-parameter
        }
    }
    catch (NoSuchFieldException ex){
        throw new MyUnitTestException(ex);
    }
    catch (IllegalAccessException ex){
        throw new MyUnitTestException(ex);
    }
    catch (IllegalArgumentException ex){
        throw new MyUnitTestException(ex);
    }
}

这是UnitTest:

@Test
public void testSetIntFields(){
    MyClass myClassInstance = new MyClass();
    final int value = 5;
    for(int nr = 1; nr <= 4; nr++){
        String nameOfField = "myInt" + nr;

        try {
            TestMethodsClass.setPrivateField(myClassInstance, nameOfField, value);
        }
        catch (MyUnitTestException ex) {
            Assert.fail("setPrivateField caused an Exception: " + ex.getThrownException());
        }

        int x = myClassInstance.getMyInt(nr);

        Assert.assertTrue("myInt " + nr + " should be above 0", x > 0);
        Assert.assertEquals("myInt " + nr + " should equal the set value (" + value + ")", value, x);
    }
}

使用以下MyClass:

@SuppressWarnings("unused")
public class MyClass
{
    private int myInt1 = 0;
    private static int myInt2 = 0;
    private final int myInt3 = 0;
    private static final int myInt4 = 0;

    public MyClass(){ }

    public int getInt(int nr){
        switch(nr){
            case 1:
                return myInt1;
            case 2:
                return myInt2;
            case 3:
                return myInt3;
            case 4:
                return myInt4;
        }
        return -1;
    }
}

(以及以下MyUnitTestException):

public class MyUnitTestException extends Exception
{
    private static final long serialVersionUID = 1L;

    private Throwable thrownException;

    public MyUnitTestException(Throwable ex){
        super(ex);
        thrownException = ex;
    }

    public String getThrownException(){
        if(thrownException != null)
            return thrownException.getClass().getName();
        else
            return null;
    }
}

将该值设置到田间地头myInt1myInt2myInt3作品,但是myInt4我发现了一个IllegalAccessException

有谁知道我应该如何在我的setPrivateField方法中解决此问题?因此,它不仅能设置privateprivate staticprivate final领域,还private static final的。


编辑1:

阅读本文禁止Java行为:在运行时更新有关内联的final和static final字段后,我将UnitTest修改为:

@Test
public void testSetIntFields(){
    MyClass myClassInstance = new MyClass();
    final int value = 5;
    for(int nr = 1; nr <= 4; nr++){
        String nameOfField = "myInt" + nr;

        try {
            TestMethodsClass.setPrivateField(myClassInstance, nameOfField, value);
        }
        catch (MyUnitTestException ex) {
            Assert.fail("setPrivateField caused an Exception: " + ex.getThrownException());
        }

        // Get the set value using reflection
        // WARNING: Since at RunTime in-lining occurs, we never use a Getter to test the set value, but instead use reflection again
        int x = -1;
        try {
            x = (Integer)TestMethodsClass.getPrivateField(myClassInstance, nameOfField);
        }
        catch (MyUnitTestException ex) {
            Assert.fail("getPrivateField caused an Exception: " + ex.getThrownException());
        }

        Assert.assertTrue("myInt " + nr + " should be above 0", x > 0);
        Assert.assertEquals("myInt " + nr + " should equal the set value (" + value + ")", value, x);
    }
}

(这是我的getPrivateField方法,该方法已经过全面测试并且可以正常工作):

// Test method to access a private Field from a class
public static Object getPrivateField(Object ob, String fieldName) throws MyUnitTestException{
    Object returnObject = null;
    try {
        Field field = ob.getClass().getDeclaredField(fieldName);
        if(field != null){
            field.setAccessible(true);
            returnObject = field.get(ob); // static fields ignore the given Object-parameter
        }
    }
    catch (NoSuchFieldException ex) {
        throw new MyUnitTestException(ex);
    }
    catch (IllegalAccessException ex) {
        throw new MyUnitTestException(ex);
    }
    catch (IllegalArgumentException ex) {
        throw new MyUnitTestException(ex);
    }
    return returnObject;
}

但是我仍然遇到同样的错误。


编辑2:

因为我在其上方的UnitTest中使用了getPrivateField并同时测试了所有UnitTest,所以它没有用。当我分别测试上面的UnitTest时,它确实起作用了。.因此,我删除了getPrivateField-UnitTests(因为在上面的代码中,我在一个测试中同时使用了Set和Get),现在它可以工作了。

我知道这对于UnitTests是非常糟糕的做法,但是无论如何,在运行时更改私有static final字段已经是不好的做法。我只是制作了一个类来获取和设置私有字段,方法和构造函数,因为在某些UnitTests中它需要大约3-4次,然后我很好奇您可以进行反射并为我的所有内容创建一个TestCase可以想到的。(我个人觉得有点太远了。)

警告:除测试外,请勿在其他任何情况下使用反射。我不建议您在常规项目中使用它,除非您尝试了其他所有可能的方法。(我什至无法想到除了测试之外,您还想在项目中使用反射的情况。)

提洛

原始静态final字段被特殊对待。

编译器将其内联为常量。最终的可执行文件不再在运行时访问该字段。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

对静态资产进行版本控制不适用于Spring Web Flow

如何从自动装配服务初始化私有静态最终版

空白的最终字段是否适用于局部变量?

最终的铸造概念不适用于过载

用反射设置私有静态最终字段

使用Java反射更改私有静态最终字段

Android:使用Java反射更改私有静态最终字段

Spring AOP适用于所有非最终方法

静态表生成适用于GCC,但不适用于clang;叮当响了吗?

解决“公共静态最终版本”安全漏洞

输入字段不适用于Windows构建版本

“最终版本”是“开发版本”吗?

静态文件不适用于GAE

空对象模式-公共静态最终版本

机架静态不适用于地图

Ajax自动填充不适用于动态字段,但适用于静态字段

静态IP设置不适用于仅主机网络

CSS动画不适用于所有版本的IE

适用于Azure虚拟机的静态公共IP VS静态私有IP

带有Maven插件的Eclipse不适用于最新版本,但适用于较旧的版本

HTML / JS拖放内容适用于笔记本电脑版本,但不适用于移动视图

适用于 PHP 但不适用于脚本

Android WorkManager 使用调试版本但不适用于发布版 APK

Android - 创建文件适用于 Android 5 但不适用于 Android 6 或更高版本

反应最终形式不适用于打字稿

最终签名的 apk 输出不适用于所有 android 设备

代码适用于静态 PHP 值,但不适用于动态变量

SiwftUI 预览版不适用于 MacOS 版本,而适用于 iOS 版本

Express static 适用于 index.html 但不适用于其他静态页面