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


97

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

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

답변:


95

문제는 리터럴 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샘플에 방법과 그 목적을 나열했다면 다른 사람들에게 도움이 될 것 입니다.
Arun Kumar

5
@ArunKumar asList()java.util.Arrays클래스 에서 정적 가져 오기입니다 . 나는 그것이 명백하다고 생각했다. 지금 생각하고 있지만 Arrays.toString()목록으로 변환되는 유일한 이유는 예쁘게 인쇄되기 때문일 것입니다.
Mike Deck

23

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

foo((Object) null);

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


실제로는 내 게시물을 참조하십시오.
Buhake Sindi

죄송합니다. 여기도 마찬가지입니다. 미안합니다. 자정 기름입니다.
Buhake Sindi

6

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

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

}

이것을 JUnit 테스트로 실행하면 Java와의 차이점이 굵게 강조 표시된 다음 결과가 생성됩니다.

sendNothing () : 수신 된 'x'는 크기 0의 배열입니다.
sendNullWithNoCast () : 수신 된 'x'가 null입니다.
sendNullWithCastToString () : 수신 된 'x'가 null입니다.
sendNullWithCastToArray () : 수신 된 'x'가 null입니다.
sendOneValue () : 수신 된 'x'는 크기 1의 배열입니다.
sendThreeValues ​​() : 수신 된 'x'는 크기 3의 배열입니다.
sendArray () : 수신 된 'x'는 크기 3의 배열입니다.

이것은 또한 매개 변수없이 variadic 함수를 호출하는 것을 고려합니다
bebbo

3

이는 일련의 배열 요소가 아닌 실제 배열로 varargs 메서드를 호출 할 수 있기 때문입니다. 애매 모호한 null자체 를 제공 null하면 Object[]. nullto 캐스팅하면 Object이 문제가 해결됩니다.


1

나는 선호한다

foo(new Object[0]);

Null 포인터 예외를 방지합니다.

도움이되기를 바랍니다.


14
글쎄, 그것이 vararg 메소드라면 왜 그냥 그렇게 부르지 foo()않습니까?
BrainStorm.exe 2014 년

@ BrainStorm.exe 귀하의 답변은 답변으로 표시되어야합니다. 감사.
MDP

1

메서드 오버로드 해결 순서는 다음과 같습니다 ( https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.12.2 ) :

  1. 첫 번째 단계는 boxing 또는 unboxing 변환 또는 가변 arity 메서드 호출 사용을 허용하지 않고 오버로드 해결을 수행합니다. 이 단계에서 적용 가능한 방법을 찾지 못하면 처리는 두 번째 단계로 계속됩니다.

    이는 Java SE 5.0 이전에 Java 프로그래밍 언어에서 유효했던 모든 호출이 가변 arity 메소드 도입, 암시 적 복싱 및 / 또는 unboxing의 결과로 모호한 것으로 간주되지 않음을 보장합니다. 그러나 변수 arity 메서드 (§8.4.1)의 선언은 주어진 메서드 메서드 호출 식에 대해 선택한 메서드를 변경할 수 있습니다. 첫 번째 단계에서는 가변 arity 메서드가 고정 된 arity 메서드로 취급되기 때문입니다. 예를 들어, 이미 m (Object)를 선언 한 클래스에서 m (Object ...)를 선언하면 m (Object []와 같은 일부 호출 표현식 (예 : m (null))에 대해 m (Object)가 더 이상 선택되지 않습니다. )가 더 구체적입니다.

  2. 두 번째 단계에서는 boxing 및 unboxing을 허용하는 동안 과부하 해결을 수행하지만 여전히 가변 arity 메서드 호출을 사용할 수 없습니다. 이 단계에서 적용 가능한 방법을 찾지 못하면 처리는 세 번째 단계로 계속됩니다.

    이렇게하면 고정 된 arity 메서드 호출을 통해 적용 할 수있는 경우 가변 arity 메서드 호출을 통해 메서드가 선택되지 않습니다.

  3. 세 번째 단계에서는 오버로딩을 가변 arity 메서드, boxing 및 unboxing과 결합 할 수 있습니다.

foo(null)일치 foo(Object... arg)arg = null첫 단계이다. arg[0] = null결코 일어나지 않는 세 번째 단계가 될 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.