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


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 StringInteger. 이 시나리오에서는 "가장 구체적인"매개 변수 (및에서 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.