65

我试过这个并从 JAVA 得到奇怪的行为,有人可以为我解释一下吗?

boolean testNull(String... string) {
    if(string == null) {
        return true;
    } else {
        System.out.println(string.getClass());
        return false;
    }
}

boolean callTestNull(String s) {
    return testNull(s);
}

然后我有测试用例:

    @Test
    public void test_cases() {
        assertTrue(instance.testNull(null)); // NULL
        assertFalse(instance.testNull()); // NOT NULL
        assertFalse(instance.callTestNull(null)); // NOT NULL
    }

问题是如果我testNull()直接用参数调用null,我会true回来,但如果callTestNull()null调用testNull(),它告诉我参数不是空的,而是空数组。

4

1 回答 1

69

问题是,如果我直接用参数 null 调用 testNull(),我会返回 true,但是如果用 null 调用 callTestNull(),它调用 testNull(),它会告诉我参数不是 null,而是空数组。

是的。如果你用编译时类型的参数调用它,String编译器知道它不能是 a String[],所以它将它包装在一个字符串数组中。所以这:

String x = null;
testNull(x);

相当于:

String x = null;
testNull(new String[] { x });

此时,(误导性命名的)string参数将具有非空值 - 相反,它将引用一个大小为 1 的数组,其唯一元素是空引用。

但是,当您null直接在方法调用中使用字面量时,它可以直接转换为String[],因此不会执行包装。

JLS 第 15.12.4.2 节

如果被调用的方法是一个可变数量的方法 m,它必然有 n > 0 个形式参数。对于某些 T,m 的最终形式参数必然具有 T[] 类型,并且 m 必然会被 k ≥ 0 个实参表达式调用。

如果使用 k ≠ n 实际参数表达式调用 m,或者,如果使用 k = n 实际参数表达式调用 m 并且第 k 个参数表达式的类型与 T[] 的赋值不兼容,则参数列表(e1, ..., en-1, en, ..., ek) 被评估为好像写成 (e1, ..., en-1, new |T[]| { en, ... , ek }), 其中 |T[]| 表示 T[] 的擦除(第 4.6 节)。

(强调我的。)

我强调的一点是为什么包装发生在参数的编译时类型是String,而不是 null 类型时。

于 2013-07-17T14:41:47.227 回答