提问人:pathikrit 提问时间:10/27/2010 最后编辑:pathikrit 更新时间:2/1/2021 访问量:37394
使用单个 null 参数调用 Java varargs 方法?
Calling Java varargs method with single null argument?
问:
如果我有一个 vararg Java 方法并且我调用 ,我有 和 as s。但是如果我调用 ,它本身是空的。为什么会这样?foo(Object ...arg)
foo(null, null)
arg[0]
arg[1]
null
foo(null)
arg
我应该怎么称呼它呢?foo
foo.length == 1 && foo[0] == null
true
答:
您需要一个显式强制转换:Object
foo((Object) null);
否则,假定参数是 varargs 表示的整个数组。
评论
这是因为 varargs 方法可以使用实际数组而不是一系列数组元素来调用。当您单独为它提供模棱两可时,它假定 是 .强制转换 to 将解决此问题。null
null
Object[]
null
Object
问题在于,当您使用文本 null 时,Java 不知道它应该是什么类型。它可以是 null Object,也可以是 null Object 数组。对于单个参数,它假定后者。
您有两个选择。将 null 显式转换为 Object,或使用强类型变量调用该方法。请参阅以下示例:
public class Temp{
public static void main(String[] args){
foo("a", "b", "c");
foo(null, null);
foo((Object)null);
Object bar = null;
foo(bar);
}
private static void foo(Object...args) {
System.out.println("foo called, args: " + asList(args));
}
}
输出:
foo called, args: [a, b, c]
foo called, args: [null, null]
foo called, args: [null]
foo called, args: [null]
评论
asList
asList()
java.util.Arrays
Arrays.toString()
一个测试用例来说明这一点:
带有 vararg-taking 方法声明的 Java 代码(恰好是静态的):
public class JavaReceiver {
public static String receive(String... x) {
String res = ((x == null) ? "null" : ("an array of size " + x.length));
return "received 'x' is " + res;
}
}
这个 Java 代码(一个 JUnit4 测试用例)调用了上面的内容(我们使用测试用例不是为了测试任何东西,只是为了生成一些输出):
import org.junit.Test;
public class JavaSender {
@Test
public void sendNothing() {
System.out.println("sendNothing(): " + JavaReceiver.receive());
}
@Test
public void sendNullWithNoCast() {
System.out.println("sendNullWithNoCast(): " + JavaReceiver.receive(null));
}
@Test
public void sendNullWithCastToString() {
System.out.println("sendNullWithCastToString(): " + JavaReceiver.receive((String)null));
}
@Test
public void sendNullWithCastToArray() {
System.out.println("sendNullWithCastToArray(): " + JavaReceiver.receive((String[])null));
}
@Test
public void sendOneValue() {
System.out.println("sendOneValue(): " + JavaReceiver.receive("a"));
}
@Test
public void sendThreeValues() {
System.out.println("sendThreeValues(): " + JavaReceiver.receive("a", "b", "c"));
}
@Test
public void sendArray() {
System.out.println("sendArray(): " + JavaReceiver.receive(new String[]{"a", "b", "c"}));
}
}
将其作为 JUnit 测试运行会产生:
sendNothing(): received 'x' is an array of size 0 sendNullWithNoCast(): received 'x' is null sendNullWithCastToString(): received 'x' is an array of size 1 sendNullWithCastToArray(): received 'x' is null sendOneValue(): received 'x' is an array of size 1 sendThreeValues(): received 'x' is an array of size 3 sendArray(): received 'x' is an array of size 3
为了让这更有趣,让我们从 Groovy 2.1.2 调用函数,看看会发生什么。原来结果不一样!不过,这可能是一个错误。receive()
import org.junit.Test
class GroovySender {
@Test
void sendNothing() {
System.out << "sendNothing(): " << JavaReceiver.receive() << "\n"
}
@Test
void sendNullWithNoCast() {
System.out << "sendNullWithNoCast(): " << JavaReceiver.receive(null) << "\n"
}
@Test
void sendNullWithCastToString() {
System.out << "sendNullWithCastToString(): " << JavaReceiver.receive((String)null) << "\n"
}
@Test
void sendNullWithCastToArray() {
System.out << "sendNullWithCastToArray(): " << JavaReceiver.receive((String[])null) << "\n"
}
@Test
void sendOneValue() {
System.out << "sendOneValue(): " + JavaReceiver.receive("a") << "\n"
}
@Test
void sendThreeValues() {
System.out << "sendThreeValues(): " + JavaReceiver.receive("a", "b", "c") << "\n"
}
@Test
void sendArray() {
System.out << "sendArray(): " + JavaReceiver.receive( ["a", "b", "c"] as String[] ) << "\n"
}
}
将其作为 JUnit 测试运行会产生以下结果,与 Java 的区别以粗体突出显示。
sendNothing(): received 'x' is an array of size 0 sendNullWithNoCast(): received 'x' is null sendNullWithCastToString(): received 'x' is null sendNullWithCastToArray(): received 'x' is null sendOneValue(): received 'x' is an array of size 1 sendThreeValues(): received 'x' is an array of size 3 sendArray(): received 'x' is an array of size 3
评论
我更喜欢
foo(new Object[0]);
以避免 Null 指针异常。
希望对你有所帮助。
评论
foo()
方法重载解决的顺序为 (https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.12.2):
第一阶段执行重载解决,不允许装箱或拆箱转换,或使用可变 arity 方法调用。如果在此阶段没有找到适用的方法,则处理将继续到第二阶段。
这保证了在 Java SE 5.0 之前的 Java 编程语言中有效的任何调用都不会因引入可变 arity 方法、隐式装箱和/或取消装箱而被视为模棱两可。但是,变量 arity 方法 (§8.4.1) 的声明可以更改为给定方法方法调用表达式选择的方法,因为变量 arity 方法在第一阶段被视为固定的 arity 方法。例如,在已经声明 m(Object) 的类中声明 m(Object...) 会导致 m(Object) 不再被选为某些调用表达式(例如 m(null)),因为 m(Object[]) 更具体。
第二阶段执行重载解析,同时允许装箱和取消装箱,但仍排除使用可变 arity 方法调用。如果在此阶段没有找到适用的方法,则处理将继续进行到第三阶段。
这确保了如果某个方法通过固定 arity 方法调用适用,则永远不会通过变量 arity 方法调用来选择该方法。
第三阶段允许将重载与可变 arity 方法、装箱和拆箱相结合。
foo(null)
与第一阶段匹配。 将是第三阶段,这永远不会发生。foo(Object... arg)
arg = null
arg[0] = null
评论