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

时间:2010-10-26 21:10:09

标签: null language-design java variadic-functions

如果我有一个vararg Java方法foo(Object ...arg)而我致电foo(null, null),我arg[0]arg[1]都为null。但是如果我拨打foo(null)arg本身就是空的。为什么会这样?

如何致电foofoo.length == 1 && foo[0] == nulltrue

6 个答案:

答案 0 :(得分:90)

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

答案 1 :(得分:22)

您需要明确转换为Object

foo((Object) null);

否则,该参数被假定为varargs所代表的整个数组。

答案 2 :(得分:5)

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

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

答案 3 :(得分:3)

这是因为可以使用实际数组而不是一系列数组元素调用varargs方法。当您为其提供含糊不清的null时,它会假定nullObject[]。将null投射到Object会解决此问题。

答案 4 :(得分:1)

我更喜欢

foo(new Object[0]);

避免空指针异常。

希望它有所帮助。

答案 5 :(得分:1)

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

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

         

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

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

         

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

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

  6.   

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