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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4028059/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 08:41:21  来源:igfitidea点击:

Calling Java varargs method with single null argument?

nulllanguage-designjavavariadic-functions

提问by pathikrit

If I have a vararg Java method foo(Object ...arg)and I call foo(null, null), I have both arg[0]and arg[1]as nulls. But if I call foo(null), argitself is null. Why is this happening?

如果我有一个可变参数 Java 方法foo(Object ...arg)并且我调用了foo(null, null),那么我同时拥有arg[0]arg[1]作为nulls。但是如果我调用foo(null)arg它本身就是空的。为什么会这样?

How should I call foosuch that foo.length == 1 && foo[0] == nullis true?

我应该如何称呼foo这样foo.length == 1 && foo[0] == null的人true

采纳答案by Mike Deck

The issue is that when you use the literal null, Java doesn't know what type it is supposed to be. It could be a null Object, or it could be a null Object array. For a single argument it assumes the latter.

问题是当您使用文字 null 时,Java 不知道它应该是什么类型。它可能是一个空对象,也可能是一个空对象数组。对于单个参数,它假定后者。

You have two choices. Cast the null explicitly to Object or call the method using a strongly typed variable. See the example below:

你有两个选择。将 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));
   }
}

Output:

输出:

foo called, args: [a, b, c]
foo called, args: [null, null]
foo called, args: [null]
foo called, args: [null]

回答by Bozho

You need an explicit cast to Object:

您需要显式转换为Object

foo((Object) null);

Otherwise the argument is assumed to be the whole array that the varargs represents.

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

回答by ColinD

This is because a varargs method can be called with an actual array rather than a series of array elements. When you provide it with the ambiguous nullby itself, it assumes the nullis an Object[]. Casting the nullto Objectwill fix this.

这是因为可以使用实际数组而不是一系列数组元素调用 varargs 方法。当您单独为它提供 ambiguous 时null,它假定nullObject[]。铸造nulltoObject将解决这个问题。

回答by David Tonhofer

A Test Case to illustrate this:

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

The Java code with a vararg-taking method declaration (which happens to be static):

带有可变参数方法声明的 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;
    }
}

This Java code (a JUnit4 test case) calls the above (we are using the test case not to test anything, just to generate some output):

这个 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"}));
    }
}

Running this as a JUnit test yields:

将此作为 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

To make this more interesting, let's call the receive()function from Groovy 2.1.2 and see what happens. It turns out that the results are not the same! This may be a bug though.

为了让这更有趣,让我们调用receive()Groovy 2.1.2 中的函数,看看会发生什么。原来结果不一样啊!虽然这可能是一个错误。

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"
    }

}

Running this as a JUnit test yields the following, with the difference to Java highlighted in bold.

将此作为 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

回答by Deepak Garg

I prefer

我更喜欢

foo(new Object[0]);

to avoid Null pointer exceptions.

避免空指针异常。

Hope it helps.

希望能帮助到你。

回答by Alexey Romanov

The ordering for method overloading resolution is (https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.12.2):

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

  1. The first phase performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.

    This guarantees that any calls that were valid in the Java programming language before Java SE 5.0 are not considered ambiguous as the result of the introduction of variable arity methods, implicit boxing and/or unboxing. However, the declaration of a variable arity method (§8.4.1) can change the method chosen for a given method method invocation expression, because a variable arity method is treated as a fixed arity method in the first phase. For example, declaring m(Object...) in a class which already declares m(Object) causes m(Object) to no longer be chosen for some invocation expressions (such as m(null)), as m(Object[]) is more specific.

  2. The second phase performs overload resolution while allowing boxing and unboxing, but still precludes the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the third phase.

    This ensures that a method is never chosen through variable arity method invocation if it is applicable through fixed arity method invocation.

  3. The third phase allows overloading to be combined with variable arity methods, boxing, and unboxing.

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

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

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

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

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

foo(null)matches foo(Object... arg)with arg = nullin the first phase. arg[0] = nullwould be the third phase, which never happens.

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