[java] 매개 변수에서 Java 유형 승격

나는이 발췌 문장을 우연히 발견했다.

public class ParamTest {
    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, long b) {
        System.out.println("In long " + (a + b));
    }

    public static void printSum(double a, long b) {
        System.out.println("In doubleLONG " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

컴파일 오류가 발생합니다.

오류 : (15, 9) java : printSum에 대한 참조가 ParamTest의 printSum (int, double) 메소드와 ParamTest 일치의 printSum (long, long) 메소드 모두 모호합니다.

이것은 어떻게 모호합니까? 이 경우 첫 번째 매개 변수가 이미 int이므로 두 번째 매개 변수 만 승격시켜야합니까? 이 경우 첫 번째 매개 변수를 승격시킬 필요가 없습니까?

다른 방법을 추가하기 위해 코드를 업데이트하면 컴파일이 성공합니다.

public static void printSum(int a, long b) {
    System.out.println(String.format("%s, %s ", a, b));
}

명확히하기 위해 확장하겠습니다. 아래 코드는 애매 모호합니다.

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, long b) {
        System.out.println("In long " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

그런 다음이 코드는 아래 모호한 결과 :

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(double a, long b) {
        System.out.println("In doubleLONG " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

그러나 이것은 모호성을 초래 하지 않습니다 .

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, double b) {
        System.out.println("In longDBL " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}



답변

나는 이것이 15.12.2.5 에 관한 JLS의 특정 규칙과 관련이 있다고 생각합니다 . 가장 구체적인 방법 선택 . 그 내용은 다음과 같습니다.

둘 이상의 멤버 메소드가 액세스 가능하고 메소드 호출에 적용 가능한 경우, 런타임 메소드 디스패치에 대한 설명자를 제공 할 멤버를 선택해야합니다. Java 프로그래밍 언어는 가장 구체적인 방법을 선택한다는 규칙을 사용합니다.

Java가 가장 구체적인 방법을 선택하는 방법 은 다음 텍스트에 자세히 설명되어 있습니다.

비공식적 인 직관은 첫 번째 방법으로 처리 된 호출이 컴파일 타임 오류없이 다른 방법으로 전달 될 수있는 경우 한 방법이 다른 방법보다 더 구체적이라는 것입니다. 명시 적으로 유형이 지정된 람다 식 인수 (§15.27.1) 또는 변수 arity 호출 (§15.12.2.4)과 같은 경우, 하나의 서명을 다른 서명에 적용 할 수있는 유연성이 있습니다.

예제의 경우 모든 메소드에 액세스하여 메소드 호출에 적용 할 수 있으므로 Java가 가장 구체적인 메소드를 결정해야합니다 .

이러한 방법의 경우 더 구체적으로 결정할 수있는 것은 없습니다.

public static void printSum(int a, double b) {
    System.out.println("In intDBL " + (a + b));
} // int, double cannot be passed to long, long or double, long without error

public static void printSum(long a, long b) {
    System.out.println("In long " + (a + b));
} // long , long cannot be passed to int, double or double, long without error

public static void printSum(double a, long b) {
    System.out.println("In doubleLONG " + (a + b));
} // double, long cannot be passed to int, double or long, long without error

네 번째 방법은 가장 구체적으로 필요한 조건을 충족시키기 때문에 모호성을 정확하게 제거합니다 .

public static void printSum(int a, long b) {
    System.out.println(String.format("%s, %s ", a, b));
}

즉, (int, long)은 컴파일 오류없이 (int, double), (long, long) 또는 (double, long)으로 전달 될 수 있습니다.


답변

실제로 매우 흥미로운 질문입니다. Java 언어 사양을 단계별로 살펴 보겠습니다.

  1. 컴파일러가 잠재적으로 적용 가능한 메소드를 식별하려고 할 때 가장 먼저 수행하는 작업은 Strict Invocation에서 적용 가능한 메소드를 검색하는 것 입니다.

  2. 귀하의 경우 그러한 방법이 없으므로 다음 단계는 Loose Invocation에서 적용 가능한 방법찾는 것입니다

  3. 이 시점에서 모든 메소드가 일치하므로 느슨한 호출로 적용 가능한 메소드 중에서 가장 구체적인 메소드 ( §15.12.2.5 )가 선택됩니다.

이것은 중요한 순간이므로 이것을 자세히 살펴 보겠습니다.

다음 중 하나에 해당하는 경우 인수 표현식 e1, …, ek를 사용하여 호출하는 경우 적용 가능한 하나의 메소드 m1이 다른 적용 가능한 메소드 m2보다 더 구체적입니다.

(다음과 같은 경우에만 관심이 있습니다) :

  • m2는 일반이 아니며 m1 및 m2는 엄격하거나 느슨한 호출로 적용 가능하며 m1에 형식 매개 변수 유형 S1, …, Sn 및 m2가 형식 매개 변수 유형 T1, …, Tn을 갖는 경우 Si 유형이 더 많습니다 모든 i에 대해 인수 ei에 대해 Ti에 특정 적입니다 (1 ≤ i ≤ n, n = k).

간단히 말해서, 모든 매개 변수 유형이 더 구체적 이면 메소드가 더 구체적 입니다. 과

S <: T ( §4.10 ) 인 경우 S 유형은 모든 표현식에 대해 T 유형보다 더 구체적 입니다.

식은 의 하위 유형 S <: T임을 의미합니다 . 프리미티브의 경우 다음 관계가 있습니다.ST

double > float > long > int

따라서 여러분의 방법을 살펴보고 어떤 방법이 다른 방법보다 더 구체적인지 살펴 보겠습니다.

public static void printSum(int a, double b) {  // method 1
    System.out.println("In intDBL " + (a + b));
}

public static void printSum(double a, long b) { // method 2
    System.out.println("In doubleLONG " + (a + b));
}

이 예제에서 메소드 1의 첫 번째 매개 변수는 분명히 메소드 2의 첫 번째 매개 변수보다 더 구체적입니다 (정수 값으로 호출하는 경우 🙂 printSum(1, 2). 그러나 두 번째 매개 변수는 방법이 더 특이 하기 때문에 long < double. 따라서이 방법들 중 어느 것도 다른 것보다 더 구체적인 것은 없습니다. 그렇기 때문에 여기에 모호성이 있습니다.

다음 예에서

public static void printSum(int a, double b) { // method 1
    System.out.println("In intDBL " + (a + b));
}

public static void printSum(long a, double b) { // method 2
    System.out.println("In longDBL " + (a + b));
}

방법 1의 첫 번째 매개 변수 유형은 방법 2의 매개 변수 유형보다 더 구체적입니다 int < long. 두 번째 매개 변수 유형이 두 유형 모두 동일하기 때문에 방법 1이 선택되는 이유입니다.


답변

int 값은 Java에서 double로 간주 될 수 있기 때문입니다. 의미 double a = 3는 유효하고 길다. long b = 3그래서 모호함을 만드는 이유이다. 당신은 전화

printSum(1, 2);

이 세 가지가 모두 유효하기 때문에 세 가지 방법 모두에 대해 혼란스러워합니다.

int a = 1;
double b =1;
long c = 1;

L을 끝에 두어 긴 값을 지정할 수 있습니다. 예를 들면 다음과 같습니다.

printSum(1L, 2L);

두 배로 변환해야합니다.

printSum((double)1, 2L);

@Erwin Bolwidt의 의견도 읽으십시오


답변