使用单个 null 参数调用 Java varargs 方法?

Calling Java varargs method with single null argument?

提问人:pathikrit 提问时间:10/27/2010 最后编辑:pathikrit 更新时间:2/1/2021 访问量:37394

问:

如果我有一个 vararg Java 方法并且我调用 ,我有 和 as s。但是如果我调用 ,它本身是空的。为什么会这样?foo(Object ...arg)foo(null, null)arg[0]arg[1]nullfoo(null)arg

我应该怎么称呼它呢?foofoo.length == 1 && foo[0] == nulltrue

语言设计 java variadic-functions

评论


答:

29赞 Bozho 10/27/2010 #1

您需要一个显式强制转换:Object

foo((Object) null);

否则,假定参数是 varargs 表示的整个数组。

评论

0赞 Buhake Sindi 10/27/2010
哎呀,这里也一样......对不起,午夜油。
3赞 ColinD 10/27/2010 #2

这是因为 varargs 方法可以使用实际数组而不是一系列数组元素来调用。当您单独为它提供模棱两可时,它假定 是 .强制转换 to 将解决此问题。nullnullObject[]nullObject

103赞 Mike Deck 10/27/2010 #3

问题在于,当您使用文本 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]

评论

0赞 Arun Kumar 6/19/2014
如果您在示例中列出了该方法及其用途,将对其他人有所帮助。asList
6赞 Mike Deck 6/20/2014
@ArunKumar 是类的静态导入。我只是认为这是显而易见的。虽然现在我正在考虑它,但我可能应该只是使用它,因为它被转换为列表的唯一原因是它会打印得很漂亮。asList()java.util.ArraysArrays.toString()
0赞 Gerardo Cauich 3/5/2022
其他参考:stackoverflow.com/a/55580650/1868371
7赞 David Tonhofer 5/7/2013 #4

一个测试用例来说明这一点:

带有 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

评论

0赞 bebbo 11/20/2019
这还考虑调用不带参数的可变调参数函数
1赞 Deepak Garg 11/26/2013 #5

我更喜欢

foo(new Object[0]);

以避免 Null 指针异常。

希望对你有所帮助。

评论

15赞 Sir Intellegence-BrainStormexe 3/11/2014
好吧,如果它是一个 vararg 方法,为什么不直接称呼它为 ?foo()
0赞 M D P 9/28/2016
@BrainStorm.exe您的答案应标记为答案。谢谢。
2赞 Alexey Romanov 4/9/2019 #6

方法重载解决的顺序为 (https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.12.2):

  1. 第一阶段执行重载解决,不允许装箱或拆箱转换,或使用可变 arity 方法调用。如果在此阶段没有找到适用的方法,则处理将继续到第二阶段。

    这保证了在 Java SE 5.0 之前的 Java 编程语言中有效的任何调用都不会因引入可变 arity 方法、隐式装箱和/或取消装箱而被视为模棱两可。但是,变量 arity 方法 (§8.4.1) 的声明可以更改为给定方法方法调用表达式选择的方法,因为变量 arity 方法在第一阶段被视为固定的 arity 方法。例如,在已经声明 m(Object) 的类中声明 m(Object...) 会导致 m(Object) 不再被选为某些调用表达式(例如 m(null)),因为 m(Object[]) 更具体。

  2. 第二阶段执行重载解析,同时允许装箱和取消装箱,但仍排除使用可变 arity 方法调用。如果在此阶段没有找到适用的方法,则处理将继续进行到第三阶段。

    这确保了如果某个方法通过固定 arity 方法调用适用,则永远不会通过变量 arity 方法调用来选择该方法。

  3. 第三阶段允许将重载与可变 arity 方法、装箱和拆箱相结合。

foo(null)与第一阶段匹配。 将是第三阶段,这永远不会发生。foo(Object... arg)arg = nullarg[0] = null