2010-03-26 2 views
21

Java varargs 구현에 버그가있는 것 같습니다. 메소드가 다양한 유형의 vararg 매개 변수로 오버로드되는 경우 Java는 적절한 유형을 구별 할 수 없습니다. 그것은 나에게 The method ... is ambiguous for the type ...varargs 및 오버로드 버그가 있습니까?

다음과 같은 코드를 생각해 오류를 제공

:

public class Test 
{ 
    public static void main(String[] args) throws Throwable 
    { 
     doit(new int[]{1, 2}); // <- no problem 
     doit(new double[]{1.2, 2.2}); // <- no problem 
     doit(1.2f, 2.2f); // <- no problem 
     doit(1.2d, 2.2d); // <- no problem 
     doit(1, 2); // <- The method doit(double[]) is ambiguous for the type Test 
    } 

    public static void doit(double... ds) 
    { 
     System.out.println("doubles"); 
    } 

    public static void doit(int... is) 
    { 
     System.out.println("ints"); 
    } 
} 

docs 말 : "일반적으로, 당신은 가변 인자 방법에 과부하가 걸리지한다, 또는 프로그래머에 대한 어려울 것 어떤 오버로드가 호출되는지 알아 내라. "

그러나이 오류에 대해서는 언급하지 않았으며 어려운 것을 찾는 프로그래머는 아니지만 컴파일러입니다.

생각?

편집 - 컴파일러 : 썬 JDK 1.6.0 U18

+13

: 당신은 제네릭을 사용할 수

public static void doit(Double... ds) { for(Double currD : ds) { System.out.println(currD); } } public static void doit(Integer... is) { for(Integer currI : is) { System.out.println(currI); } } 

을 또는 :

당신은 메소드 서명 대신 래퍼 유형을 사용할 수 있습니다 : 다행히도,이 문제를 방지 할 수있는 몇 가지 방법이 있습니다 그들은 컴파일러를 작성한 프로그래머에 대해 이야기하고 있습니다. – mob

+1

버전 및 컴파일러는 무엇입니까? Eclipse 또는 JDK? –

+0

편집보기 - Sun jdk 1.6.0 u18 – pstanton

답변

7

at the Sun Forums 이상 이에 대한 토론이 있습니다.

실제 해상도가 없으면 그냥 사임합니다.

Varargs (및 auto-boxing, 특히 varargs와 함께 사용하기 힘든 행동을 유발하는 자동 권투)은 Java의 수명 후반부에 풀려 났으며 이것은 이것이 보여주는 한 영역입니다. 컴파일러보다 사양에 버그가 많습니다.

적어도 좋은 (?) SCJP 트릭 질문을 만듭니다.

+5

실제로 컴파일러 버그로 인정되어 버그가 수정되었습니다. –

+0

문제의 버그는 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6199075 –

13

문제는 모호한 것입니다.

doIt(1, 2); 

doIt(int ...), 또는 doIt(double ...)를 호출 할 수 있습니다. 후자의 경우, 정수 리터럴은 double 값으로 승격됩니다.

나는 자바 스펙이 모호한 구조라고 말하고 컴파일러는 스펙에 의해 규정 된 규칙을 따르고 있다고 확신한다. (나는 이것을 더 자세히 조사해야한다.)

EDIT - JLS의 관련 부분은 "15.12.2.5 Choosing the Most Specific Method"이지만 머리가 아플 수 있습니다.

나는 int[]double[] (반대의 경우도 마찬가지)의 하위 유형이 아니기 때문에 추론이 void doIt(int[])void doIt(double[])보다 더 구체적 (또는 그 반대)이 아님이 될 것이라고 생각합니다. 두 가지 오버로드가 동일하게 특정 적이기 때문에 호출이 모호합니다. 반대로

, void doItAgain(int) 는 상기 JLS있어서 double의 하위 유형 void doItAgain(double)int 때문에보다 구체적이다. 따라서 doItAgain(42)에 대한 호출이 모호하지 않습니다.

편집 2 - @finnw는 버그, 권리입니다. 15.12.2.5의이 부분을 고려 (비 해당하는 경우를 제거하기 위해 편집) :

하나의 변수 인수에 대응 멤버 방법이라는 m는 같은 이름의 다른 변수 인수에 대응 멤버 방법보다 더 구체적인 경우 :

하나의 멤버 메서드는 n 개의 매개 변수를 가지며 다른 멤버 메서드는 k 개의 매개 변수를 갖습니다. 여기서 n ≥ k입니다. 첫 번째 멤버 메서드의 매개 변수 형식은 T1,. . . , Tn-1, Tn [], 다른 방법의 매개 변수의 유형은 U1,. . . , Uk-1, Uk []. 하자, Si = Ui, 1 < = i < = k.그러면 : 1에서 모든 J에 대한

  • 케이-1, TJ < : Sj에, 그리고
  • 모든 J에 대한 K로부터 N까지 TJ <는 SK

이 적용 n = k = 1 인 경우에, doIt(int[])doIt(double[])보다 더 구체적임을 알 수있다.


는 사실이 에 대한 bug report이 썬은 "매우 낮음"으로 우선 순위를했다하지만 그것이 실제로 버그가 있음을 인정합니다. 버그는 이제 Java 7 (b123)에서 Fixed로 표시됩니다.

+3

에 의해 doIt (int)과 doIt (double)도 모호해야합니다 (varargs없이). – Thilo

+2

IIRC가 모호하게 만드는 것은 varargs와의 상호 작용입니다. 실제로 프로모션은 두 단계로 진행됩니다. 1) 배열에 대한 args 시퀀스 승격, 2) 정수형 리터럴을 복식으로 승격하는 것. –

+2

이것은 버그라고 생각합니다. 즉, 컴파일러의 동작이 JLS와 일치하지 않습니다. JLS의 해당 섹션을 올바르게 이해하면 doIt (int ...)은 doIt (double. ..)'왜냐하면'int'는'double'의 적절한 서브 타입이기 때문입니다. 'int []'는'double []'의 부속 유형이 아니지만 그것은 요구 사항 중 하나가 아니므로 과부하 해결에 영향을 미치지 않아야합니다. – finnw

4

흥미 롭습니다. 어쩌면

public static <T> void doit(T... ts) { 
     for(T currT : ts) { 
     System.out.println(currT); 
     } 
    } 
+2

에서 찾을 수있는 버그라고 생각합니다. 제네릭을 사용할 수는 없습니다. 아마도 코드는 int보다 두 배 이상 다른 작업을 수행합니다. – Thilo

+0

좋은 지적. 서명에서 래퍼 유형을 사용하는 방법을 고수하겠습니다. :) –

+2

재미있는 해킹. 오토 복싱과 프로모션이 같은 인수에 적용되지 않기 때문에 작동합니다. 자동 복싱을 강요하면 int-to-double 프로모션을 방지하고 단 하나의 메소드 서명 만 일치시킵니다. 이것은 예 # 3 (float 값으로)에서는 * 작동하지 않습니다. – finnw

관련 문제