매개 변수에서 Java 유형 승격


20

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

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

2
컴파일러는 호출을 매핑 할 수 있습니다 printSum (1, 2); printSum (long a, long b) 또는 printSum (int a, double b)에 모호합니다. printSum (1, 2d)
Ravindra Ranwala

6
오류 메시지를 잘못 인용했으며 그 차이가 매우 중요합니다. 실제 오류 메시지는 다음과 같습니다 Error:(15, 9) java: reference to printSum is ambiguous both method printSum(int,double) in ParamTest and method printSum(long,long) in ParamTest match.-모호한 메소드가 아니며 모호한 메소드에 대한 호출입니다.
Erwin Bolwidt

1
@ErwinBolwidt 오류 메시지는 일식에서 잘못 인용했습니다. 어쨌든 printSum (int a, long b)을 추가하면 오류 메시지가 제거되므로 여전히 이해하지 못합니다.
riruzen

2
JLS-5.3 => 느슨한 호출 컨텍스트에서 허용되는 변환으로 표현식의 유형을 매개 변수의 유형으로 변환 할 수 없으면 컴파일 타임 오류가 발생합니다. 문맥에 적용 가능한 것처럼 보이지만 실제로는 방법을 유추하기가 쉽지 않습니다. +1
Naman

1
우리는 이것에 대한 정식 질문이 분명히 필요합니다 : stackoverflow.com/… . "
Marco13

답변:


17

나는 이것이 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)으로 전달 될 수 있습니다.


2
서머 닝하기 위해 호출에 의해 모든 메소드에 액세스 할 수있는 경우 java는 메소드를 식별합니다.이 메소드는 컴파일 시간 오류없이 다른 메소드를 호출 할 수 있습니다. 나는 이것이 하나의 유효한 대답 @riruzen이라고 생각합니다.
하기 Sandeep Kokate

감사! 나는 이것을 (double, int) vs (double, long)로 시도했고 실제로 모호성을 정리했으며 (double, int) vs (long, double)은 예상대로 모호했습니다.
riruzen

7

실제로 매우 흥미로운 질문입니다. 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, double)이 (long, long)으로 모호한 지 설명하지 못합니다 (int, double)는 첫 번째 매개 변수와 관련하여 더 구체적이어야하기 때문입니다.
riruzen

3
@riruzen은 설명합니다 : double보다 구체적이지 않습니다 long. 이 방법을 선택하려면 모든 유형 매개 변수가 더 구체적이어야합니다. 모든 유형 i에 대한 인수 ei에 대해 유형 Si가 Ti보다 더 구체적이어야합니다 (1 ≤ i ≤ n, n = k)
Kirill Simonov

+1이어야합니다
Tea

0

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의 의견도 읽으십시오


예, 컴파일러는 3 가지 방법 모두를 혼동했습니다. 먼저 컴파일러는 정확한 유형을 찾은 다음 찾지 못하면 호환되는 유형을 찾으십시오.이 경우 모두 호환됩니다.
다른 코더

2
3 대신 4 개의 메소드 선언이 있으면 모호성이 사라집니다. public static void printSum (int a, double b) public static void printSum (int a, long b) public static void printSum (long a, long b) public static void printSum (double a, long b) 그래서 귀하의 답변에 동의하지만 여전히 질문에 대답하지 않습니다. 내가 실수하지 않으면 정확한 유형을 사용할 수없는 경우 Java는 자동 유형 승격을 수행합니다. 나는 그것이 왜 3과 모호하게되는지 이해하지 못한다.
riruzen
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.