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


98

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

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 값에 할당하여 유효하고 Java 및 대부분의 프로그래밍 언어의 순서가 가장 가까운 유형과 객체에 맞도록 할 수 있습니다.
— JonH


6
분명히 이것은 제목을 읽은 사람들에 의해 중복으로 닫혔습니다. 여기서 실제 질문은 "무엇이 null인지"가 아니라 특정 과부하가 선택된 이유에 대한 것입니다.
— interjay

답변:


102

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

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

String x = null;

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

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

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


3
그리고 String 오버로드는 Object 오버로드보다 어떻게 더 구체적입니까?
— user1610015

12
때문에 Object모든 유형을하고 그것을 포장 할 수 있습니다 ObjectA는 반면, String단지 걸릴 수 있습니다 String. 이 경우 String유형에 비해 유형이 더 구체적입니다 Object.
— JonH

10
@zakSyed 더 전문화 된 "문자열"또는 "개체"가 무엇인지 묻는다면 무엇을 말하겠습니까? 분명히 "문자열"맞죠? 질문을 받으면 더 전문화 된 "String"또는 "StringBuffer"가 무엇입니까? 답이 없습니다. 둘 다 직교 전문화입니다. 어떻게 둘 중에서 선택할 수 있습니까? 당신이 원하는 어느 그런 다음에 대한 명시해야합니다 (즉, 귀하는 null 참조 캐스트 question.method((String)null))
— 에드윈 Dalorzo을

1
@ JonSkeet : 말이 되네요. 따라서 기본적으로 가장 구체적인 규칙에 따라 메서드를 찾고 더 구체적인 규칙을 결정할 수 없으면 컴파일 타임 오류가 발생합니다.
— zakSyed

3
@JonH "null"은 참조 유형입니다. 메소드 중 하나가 매개 변수 (즉, int)로 기본 유형을 수신하면 널 유형의 참조를 호출 할 올바른 메소드를 선택할 때 컴파일러에서 고려하지도 않습니다. "Int"가 java.lang.Integer를 의미했거나 기본 유형 int를 의미하는 경우 혼란 스럽습니다.
— Edwin Dalorzo 2012 년

9

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

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

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

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


유형이 Void라고 확신합니다.
— xavierm02

2
@ xavierm02 Java 언어 사양에는 이에 대한 언급이 없습니다. 우리 모두가 귀하의 주장을 확인할 수 있도록 귀하의 참조를 인용 할 수 있습니까?
— Edwin Dalorzo 2012 년

나는 그것을 읽지 않는다. 하지만 이번 여름에 Java를 일부 수행했으며 Void 개체를 사용하는 메서드로 Object를 사용하는 메서드를 오버로드 할 수 있습니다.
— xavierm02

유형보다는 클래스에 가깝습니다.
— xavierm02

4
@ xavierm02 물론 가능합니다. 원하는 다른 유형으로 Java에서 메소드를 오버로드 할 수 있습니다. 그럼에도 불구하고 그것은 질문이나 내 대답과 관련이 없습니다.
— 에드윈 달로 조

2

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


2

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

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

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

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

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

아주 멋진 질문입니다!


null은 둘 다에 할당 될 수 있습니다. 이것이 차이점이며 왜 그렇지 않은지 컴파일합니다. 절대적으로 유효한 코드입니다.
— JonH

0

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

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

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


0

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 유형에 대해 모호합니다.

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


0

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


첫 번째 예에서는 문자열 객체를 확장, 그래서 "가장 구체적인는"문자열을 복용하는 방법입니다,하지만 두 번째 예에서는 문자열과 StringBuffer를 모두 가 동등하게 특정 그래서 개체를 확장하고, 따라서 컴파일러는 선택 할 수 없습니다
— 데이비드 커

-1

나는 둘 다 말할 것이다. NULL은 값이 아닌 상태입니다. 이에 대한 자세한 정보는 이 링크 를 확인하십시오 (이 기사는 SQL에 적용되지만 귀하의 질문에도 도움이된다고 생각합니다).


nullJLS에 정의 된대로 확실히 가치입니다.
— Sotirios Delimanolis 2015
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.