IT TIP

컴파일러가 char에 대해 varargs char 오버로드보다 int 오버로드를 선호하는 이유는 무엇입니까?

itqueen 2020. 11. 23. 20:44
반응형

컴파일러가 char에 대해 varargs char 오버로드보다 int 오버로드를 선호하는 이유는 무엇입니까?


암호

public class TestOverload {

    public TestOverload(int i){System.out.println("Int");}
    public TestOverload(char... c){System.out.println("char");}

    public static void main(String[] args) {
        new TestOverload('a');
        new TestOverload(65);
    }
}

산출

Int
Int

예상되는 동작입니까? 그렇다면 그 이유는 무엇입니까? 나는 기대하고있다 : char, Int

참고 : Java 8을 사용하고 있습니다.


varargs ( ...)가있는 메서드는 컴파일러가 선택할 오버로드 된 메서드를 결정할 때 가장 낮은 우선 순위를 갖습니다. 따라서 단일 매개 변수를 사용하여 호출 할 때 대신 TestOverload(int i)선택 TestOverload(char... c)됩니다. a 는 자동으로 .TestOverloadchar'a'charint

JLS 15.12.2 :

  1. 첫 번째 단계 (§15.12.2.2)는 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. 두 번째 단계 (§15.12.2.3)에서는 boxing 및 unboxing을 허용하면서 오버로드 해결을 수행하지만 가변 arity 메서드 호출의 사용은 여전히 ​​금지합니다 . 이 단계에서 적용 가능한 방법을 찾을 수없는 경우 처리는 세 번째 단계로 계속됩니다. 이렇게하면 고정 된 arity 메서드 호출을 통해 적용 가능한 경우 가변 arity 메서드 호출을 통해 메서드가 선택되지 않습니다.

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

편집하다:

컴파일러가 TestOverload(char... c)생성자 를 호출하도록 강제하려는 경우 생성자 호출 a를 전달할 수 있습니다 char[].

new TestOverload (new char[] {'a'});

예, 예상되는 동작입니다. 메서드 호출의 우선 순위는 다음과 같습니다.

  1. Widending
  2. 권투
  3. Varargs

다음은 동일한 관련 Java 문서 에서 발췌 한 것입니다 .

적용 가능성을 결정하는 프로세스는 잠재적으로 적용 가능한 방법을 결정하는 것으로 시작됩니다 (§15.12.2.1).

나머지 프로세스는 Java SE 5.0 이전의 Java 프로그래밍 언어 버전과의 호환성을 보장하기 위해 세 단계로 나뉩니다. 단계는 다음과 같습니다.

첫 번째 단계 (§15.12.2.2)는 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)가 더 이상 선택되지 않습니다. )가 더 구체적입니다.

두 번째 단계 (§15.12.2.3)에서는 boxing 및 unboxing을 허용하면서 오버로드 해결을 수행하지만 가변 arity 메서드 호출의 사용은 여전히 ​​금지합니다. 이 단계에서 적용 가능한 방법을 찾을 수없는 경우 처리는 세 번째 단계로 계속됩니다.

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

세 번째 단계 (§15.12.2.4)에서는 오버로딩을 가변 arity 방법, boxing 및 unboxing과 결합 할 수 있습니다.


Joshua Bloch의 확실한 조언 (Effective Java, 2nd Ed) :

"과격하게 다른 유형을 가진 오버로드 된 메소드에 대한 인수로만 선택하십시오."

근본적으로 다른 유형을 가진 객체는 합리적으로 다른 인수 유형으로 캐스팅 될 수없는 객체입니다. 이 규칙을 따르면 컴파일러가 컴파일 타임에 예상치 못한 메서드 오버로딩을 선택할 때 발생할 수있는 신비한 오류를 디버깅하는 시간을 잠재적으로 절약 할 수 있습니다.

코드 줄이이 규칙을 위반하고 버그의 문을 엽니 다.

public TestOverload(int i){System.out.println("Int");}
public TestOverload(char... c){System.out.println("char");}

A char는와 상호 변환이 가능 int하므로 호출에서 발생할 일을 예측할 수있는 유일한 방법은 Java 언어 사양으로 이동하여 오버로딩이 해결되는 방법에 대한 다소 난해한 규칙을 읽는 것입니다.

Luckily, this situation shouldn't need JLS research. If you have arguments that are not radically different from each other, probably the best option is to not overload. Give the methods different names so that there is no possibility for error or confusion on the part of anyone who may need to maintain the code.

Time is money.


I took the code from this link and modified some parts of it:

    public static void main(String[] args) {
    Byte i = 5;
    byte k = 5;
    aMethod(i, k);
}

//method 1
static void aMethod(byte i, Byte k) {
    System.out.println("Inside 1");
}

//method 2
static void aMethod(byte i, int k) {
    System.out.println("Inside 2");
}

//method 3
static void aMethod(Byte i, Byte k) {
    System.out.println("Inside 3 ");
}

//method 4
static void aMethod(Byte  i, Byte ... k) {
    System.out.println("Inside 4 ");
}

The compiler gives error (The method is ambiguous for the type Overloading) for methods 1, 2 and 3 but not 4 (why?)

The answer lies in the mechanism which java uses to match method calls to method signatures. The mechanism is done in three phases, in each phase if it finds matching method it stops:

+phase one: use widening to find matching method (no matching methods found)

+phase two: (also) use boxing/unboxing to find matching method (method 1,2 and 3 match)

+phase three: (also) use var args (method 4 matches!)

참고URL : https://stackoverflow.com/questions/32471499/why-does-the-compiler-prefer-an-int-overload-to-a-varargs-char-overload-for-a-ch

반응형