IT박스

단일 null 인수로 Java varargs 메서드를 호출합니까?

itboxs 2020. 9. 8. 07:47
반응형

단일 null 인수로 Java varargs 메서드를 호출합니까?


나는 가변 인자 자바 방법이 있다면 foo(Object ...arg)내가 전화를 foo(null, null), 나는 모두가 arg[0]arg[1]null들. 그러나 내가 호출 foo(null)하면 arg자체가 null입니다. 왜 이런 일이 발생합니까?

어떻게 호출해야 foo그러한 foo.length == 1 && foo[0] == null입니다 true?


문제는 리터럴 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]

다음에 대한 명시 적 캐스트가 필요합니다 Object.

foo((Object) null);

그렇지 않으면 인수는 varargs가 나타내는 전체 배열로 간주됩니다.


이를 설명하는 테스트 케이스 :

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

이 자바 코드 (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 () : 수신 된 'x'는 크기 0의 배열입니다.
sendNullWithNoCast () : 수신 된 'x'가 null입니다.
sendNullWithCastToString () : 수신 된 'x'는 크기 1의 배열입니다.
sendNullWithCastToArray () : 수신 된 'x'가 null입니다.
sendOneValue () : 수신 된 'x'는 크기 1의 배열입니다.
sendThreeValues ​​() : 수신 된 'x'는 크기 3의 배열입니다.
sendArray () : 수신 된 'x'는 크기 3의 배열입니다.

이를 더 흥미롭게 만들기 위해 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.

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

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 null by itself, it assumes the null is an Object[]. Casting the null to Object will fix this.


I prefer

foo(new Object[0]);

to avoid Null pointer exceptions.

Hope it helps.


The ordering for method overloading resolution is (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.

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

참고URL : https://stackoverflow.com/questions/4028059/calling-java-varargs-method-with-single-null-argument

반응형