[java] 매개 변수가 리터럴 널값 인 경우 오버로드 된 메소드는 어떻게 선택됩니까?

퀴즈에서이 질문을 보았습니다.

public class MoneyCalc {

   public void method(Object o) {
      System.out.println("Object Verion");
   }

   public void method(String s) {
      System.out.println("String Version");
   }

   public static void main(String args[]) {
      MoneyCalc question = new MoneyCalc();
      question.method(null);
   }
}

이 프로그램의 출력은 “String Version”입니다. 하지만 오버로드 된 메서드에 null을 전달하여 문자열 버전을 선택한 이유를 이해할 수 없었습니다. null은 아무것도 가리키는 문자열 변수입니까?

그러나 코드가 다음과 같이 변경되면

public class MoneyCalc {

   public void method(StringBuffer sb) {
      System.out.println("StringBuffer Verion");
   }

   public void method(String s) {
      System.out.println("String Version");
   }

   public static void main(String args[]) {
      MoneyCalc question = new MoneyCalc();
      question.method(null);
   }
}

“Method (StringBuffer) 메소드가 MoneyCalc 유형에 대해 모호합니다.”라는 컴파일 오류를 제공합니다.



답변

null은 아무것도 가리키는 문자열 변수입니까?

널 참조는 모든 클래스 유형의 표현식으로 변환 될 수 있습니다. 따라서의 경우 String괜찮습니다.

String x = null;

여기서 String오버로드 는 JLS의 섹션 15.12.2.5에 따라 Java 컴파일러가 가장 구체적인 오버로드를 선택하기 때문에 선택 됩니다. 특히:

비공식적 인 직관은 첫 번째 메서드가 처리 한 호출이 컴파일 타임 유형 오류없이 다른 메서드에 전달 될 수있는 경우 한 메서드가 다른 메서드보다 더 구체적이라는 것입니다.

두 번째 경우에는 두 방법 모두 여전히 적용 가능하지만 다른 방법보다 더 구체적이 String지도 않고 StringBuffer특정 방법도 다른 방법보다 구체적이지 않으므로 컴파일러 오류가 발생합니다.


답변

또한 JLS 3.10.7 은 “null”이 “null 유형”의 리터럴 값임을 선언합니다. 따라서 “null”이라는 유형이 있습니다.

나중에 JLS 4.1 에서는 변수 선언이 불가능한 null 유형이 있지만 null 리터럴을 통해서만 사용할 수 있다고 명시합니다. 나중에 다음과 같이 말합니다.

널 참조는 항상 모든 참조 유형으로 확장 참조 변환을 수행 할 수 있습니다.

컴파일러가 String으로 확장하기로 선택한 이유는 Jon의 답변 에서 설명 할 수 있습니다 .


답변

당신은 할당 할 수 있습니다 string A를 null객체에 다음이 유효하고 자바와 대부분의 프로그래밍 언어에 대한 순서가 가장 가까운 유형에 적합하고, 그래서 값.


답변

제목에있는 질문에 대답하려면 null은 (는) String이거나이거나에 대한 Object참조를 할당 할 수 있습니다.null .

사실이 코드가 컴파일된다는 사실에 놀랐습니다. 이전에 비슷한 것을 시도했는데 호출이 모호하다는 컴파일러 오류가 발생했습니다.

그러나이 경우 컴파일러는 먹이 사슬에서 가장 낮은 방법을 선택하는 것 같습니다. 당신을 돕기 위해 가장 일반적인 버전의 메소드를 원한다고 가정합니다.

그래도이 (겉보기에) 똑같은 시나리오에서 컴파일러 오류가 발생한 예를 찾아 볼 수 있는지 확인해야합니다 …]

편집 : 알겠습니다 . 내가 만든 버전에서는 a StringInteger. 이 시나리오에서는 “가장 구체적인”매개 변수 (및에서 Object와 같이 String)가 없으므로 코드에서와 달리 둘 중에서 선택할 수 없습니다.

아주 멋진 질문입니다!


답변

문자열 유형은 객체 유형보다 더 구체적입니다. Integer 유형을 취하는 메서드를 하나 더 추가한다고 가정 해 보겠습니다.

public void method(Integer i) {
      System.out.println("Integer Version");
   }

그러면 호출이 모호하다는 컴파일러 오류가 발생합니다. 이제 우리는 동일한 우선 순위를 가진 동일하게 구체적인 두 가지 방법을 사용합니다.


답변

Java 컴파일러는 대부분의 파생 클래스 유형을 제공하여 널을 할당합니다.

이를 이해하는 예는 다음과 같습니다.

class A{

    public void methodA(){
        System.out.println("Hello methodA");
    }
}

class B extends A{
    public void methodB(){
        System.out.println("Hello methodB");
    }
}

class C{
    public void methodC(){
        System.out.println("Hello methodC");
    }
}

public class MyTest {

     public static void fun(B Obj){
         System.out.println("B Class.");
     }
     public static void fun(A Obj){
         System.out.println("A Class.");
     }

    public static void main(String[] args) {
        fun(null);
    }
}

출력 : B 클래스.

반면에 :

public class MyTest {

     public static void fun(C Obj){
         System.out.println("B Class.");
     }
     public static void fun(A Obj){
         System.out.println("A Class.");
     }

    public static void main(String[] args) {
        fun(null);
    }
}

결과 : fun (C) 메소드는 MyTest 유형에 대해 모호합니다.

이 사례를 더 잘 이해하는 데 도움이되기를 바랍니다.


답변

출처 : https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5

개념 : 가장 구체적인 방법

설명 : 둘 이상의 구성원 메서드에 액세스 할 수 있고 메서드 호출에 적용 할 수있는 경우 런타임 메서드 디스패치를위한 설명자를 제공 할 하나를 선택해야합니다. Java 프로그래밍 언어는 가장 구체적인 방법이 선택되는 규칙을 사용합니다. null을 특정 유형으로 캐스팅하면 원하는 메서드가 자동으로 호출됩니다.