如何以编程方式启用断言?

35

我该如何以编程方式启用特定类的断言,而不是指定命令行参数“-ea”?

public class TestAssert {

    private static final int foo[] = new int[]{4,5,67};


    public static void main(String []args) {
        assert foo.length == 10;
    }
}
5个回答

11

尝试一下

ClassLoader loader = getClass().getClassLoader();
setDefaultAssertionStatus(true);
或。
ClassLoader.getSystemClassLoader().setDefaultAssertionStatus(true);

编辑:

基于评论

    ClassLoader loader = ClassLoader.getSystemClassLoader();
    loader.setDefaultAssertionStatus(true);
    Class<?> c = loader.loadClass("MyClass");
    MyClass myObj = (MyClass) c.newInstance();


public class MyClass {

    private static final int foo[] = new int[]{4,5,67};
    MyClass()
    {
        assert foo.length == 10;
    }
}

3
当评论被标记为过时后,“基于评论”的方法就不起作用了,因此更好的做法是正确记录原因。为什么要通过反射创建MyClass的实例? - Kalle Richter

9
这是对@bala的好答案的评论,但它变得太长了。
如果只启用断言然后调用你的主类 - 你的主类将在启用断言之前被加载,所以你可能需要一个不直接引用代码中其他任何东西的加载器。它可以设置断言,然后通过反射加载其余的代码。
如果在加载类时未启用断言,则应立即将其“编译出”,因此您将无法切换它们的开关状态。如果您想要切换它们,则根本不需要断言。
由于运行时编译,像这样的东西:
public myAssertNotNull(Object o) {
    if(checkArguments) 
        if(o == null)
            throw new IllegalArgumentException("Assertion Failed");
}

如果代码被频繁执行,并且checkArguments为false并且不会改变,那么该方法调用整个过程可能在运行时被编译掉,因此其速度几乎与断言相同(这种性能取决于虚拟机)。


1

3
虽然理论上这个回答可以回答问题,但最好在这里包含回答的关键部分,并提供链接以供参考。 - Kalle Richter

1
可以使用反射来启用或禁用断言。与反射一般一样,该解决方案很脆弱,可能不适用于所有使用场景。但是,如果适用并且可接受,它比setClassAssertionStatus更灵活,因为它允许在执行的各个点上启用/禁用断言检查,即使在类初始化之后
此技术需要生成合成静态字段的编译器来指示是否启用了断言。例如,javac和Eclipse编译器都会为包含assert语句的任何类生成$assertionsDisabled字段。
可以按以下方式验证此操作:
public class A {
    public static void main(String[] args) {
        assert false;
        System.out.println(Arrays.toString(A.class.getDeclaredFields()));
    }
}

将所需的断言状态设置为仅涉及设置此字段(请注意反转的布尔值):

// Helper method in any class
public static void setAssertionsEnabled(Class<?> clazz, boolean value) 
    throws ReflectiveOperationException
{
    Field field = clazz.getDeclaredField("$assertionsDisabled");
    field.setAccessible(true);
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    field.set(Test.class, !value);
}

在某些Java 11+ JRE中,此代码会触发五个非常丑陋的STDERR消息:WARNING: An illegal reflective access operation has occurred[...] 可以使用 --add-opens java.base/java.lang.reflect=ALL-UNNAMED 来抑制这些消息。 - Arno Unkrig

0

最简单且最好的方法可能是:

public static void assertion(boolean condition, String conditionFailureMessage)
{
    if(!condition)
        throw new AssertionError(conditionFailureMessage);
}

不需要将-ea设置为VM参数。
调用函数的方式如下:
assertion(sum>=n,"sum cannot be less than n");

如果断言失败,代码将会抛出 AssertionError,否则代码将会安全地运行。

2
断言的目的在于它们可以在JRE中被激活或不被激活,因此语句assert x可能会产生影响,也可能不会。这是您的建议所无法实现的。 - Kalle Richter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接