[java] 자바 동적 바인딩 및 메서드 재정의

어제 2 시간 동안 기술 전화 인터뷰를했지만 (우후!), Java의 동적 바인딩에 관한 다음 질문을 완전히 엉망으로 만들었습니다. 그리고 제가 몇 년 전 조교 였을 때 학부생들에게이 개념을 가르 치곤했기 때문에 두 배로 당혹 스럽습니다. 그래서 제가 그들에게 잘못된 정보를 주었다는 전망이 조금 불안합니다 …

내가받은 문제는 다음과 같습니다.

/* What is the output of the following program? */

public class Test {

  public boolean equals( Test other ) {
    System.out.println( "Inside of Test.equals" );
    return false;
  }

  public static void main( String [] args ) {
    Object t1 = new Test();
    Object t2 = new Test();
    Test t3 = new Test();
    Object o1 = new Object();

    int count = 0;
    System.out.println( count++ );// prints 0
    t1.equals( t2 ) ;
    System.out.println( count++ );// prints 1
    t1.equals( t3 );
    System.out.println( count++ );// prints 2
    t3.equals( o1 );
    System.out.println( count++ );// prints 3
    t3.equals(t3);
    System.out.println( count++ );// prints 4
    t3.equals(t2);
  }
}

출력은 재정의 된 equals()메서드 내 에서 at t1.equals(t3)t3.equals(t3). 후자의 경우는 충분히 명확하며 전자의 경우 t1Object 유형의 참조가 있더라도 Test 유형으로 인스턴스화되므로 동적 바인딩은 메소드의 재정의 된 양식을 호출해야합니다.

분명히 아닙니다. 내 면접관은 나에게 프로그램을 직접 실행하도록 격려했고, 보라, 재정의 된 메소드에서 단 하나의 출력 만 있었다 : at the line t3.equals(t3).

내 질문은, 왜? 이미 언급했듯이, t1은 Object 유형의 참조 임에도 불구하고 (정적 바인딩이 Object의 equals()메소드를 호출 함 ) 동적 바인딩 참조의 인스턴스화 된 유형을 기반으로 가장 구체적인 버전의 메소드를 호출 해야합니다 . 내가 무엇을 놓치고 있습니까?



답변

Java는 오버로드 된 메서드에 대해 정적 바인딩을 사용하고 재정의 된 메서드에 대해서는 동적 바인딩을 사용합니다. 예제에서 equals 메서드는 오버로드되었으므로 (Object.equals ()와 다른 매개 변수 유형을 가짐) 호출 된 메서드 는 컴파일 타임에 참조 유형에 바인딩됩니다 .

여기에 몇 가지 토론

equals 메서드라는 사실은 실제로 관련이 없습니다. 다른 방법으로 오버로드하는 대신 오버로드하는 것은 일반적인 실수입니다. 인터뷰에서 문제에 대한 답변을 기반으로 이미 알고 있습니다.

편집 : 여기에 좋은 설명 도 있습니다. 이 예는 대신 매개 변수 유형과 관련된 유사한 문제를 표시하지만 동일한 문제로 인해 발생합니다.

바인딩이 실제로 동적이라면 호출자와 매개 변수가 Test의 인스턴스 인 경우 재정의 된 메서드가 호출 될 것이라고 생각합니다. 따라서 t3.equals (o1)는 인쇄되지 않는 유일한 경우입니다.


답변

equals방법의는 Test무시하지 않는 equals방법을 java.lang.Object. 매개 변수 유형을보십시오! Test클래스에 과부하 equals받는 방법 Test.

equals메서드를 재정의하려는 경우 @Override 주석을 사용해야합니다. 이로 인해 이러한 일반적인 실수를 지적하는 컴파일 오류가 발생합니다.


답변

흥미롭게도 Groovy 코드 (클래스 파일로 컴파일 될 수 있음)에서는 호출 중 하나를 제외하고 모두 print 문을 실행합니다. (테스트와 객체를 비교하는 것은 분명히 Test.equals (Test) 함수를 호출하지 않을 것입니다.) 이것은 groovy가 완전히 동적 타이핑을하기 때문입니다. 이것은 명시 적으로 동적으로 입력되는 변수가 없기 때문에 특히 중요합니다. 프로그래머가 그루비가 자바 작업을 수행하기를 기대하기 때문에 이것이 유해한 것으로 간주되는 몇 곳에서 읽었습니다.


답변

Java는 반환 유형에서만 매개 변수의 공분산을 지원하지 않습니다.

즉, 재정의 메서드의 반환 유형이 재정의 된 항목의 하위 유형일 수 있지만 매개 변수에는 해당되지 않습니다.

Object의 equals에 대한 매개 변수가 Object 인 경우, 하위 클래스에 다른 항목과 equals를 넣는 것은 재정의 된 메서드가 아니라 오버로드됩니다. 따라서 해당 메서드가 호출되는 유일한 상황은 T3의 경우와 같이 매개 변수의 정적 유형이 Test 인 경우입니다.

면접 과정에 행운을 빕니다! 학생들에게 가르치는 일반적인 알고리즘 / 데이터 구조 질문 대신 이러한 유형의 질문을하는 회사에서 인터뷰를 받고 싶습니다.


답변

핵심은 equals () 메서드가 표준을 따르지 않는다는 사실에 있다고 생각합니다. Object 객체가 아닌 다른 Test 객체를 사용하므로 equals () 메서드를 재정의하지 않습니다. 이것은 실제로 Object 객체가 Object.equals (Object o)를 호출하는 동안 Test 객체가 주어 졌을 때 특별한 작업을 수행하기 위해 실제로 오버로드 한 것을 의미합니다. IDE를 통해 해당 코드를 보면 Test에 대한 두 개의 equals () 메서드가 표시됩니다.


답변

메서드가 재정의되지 않고 오버로드됩니다. Equals는 항상 Object를 매개 변수로 사용합니다.

btw, Bloch의 효과적인 자바에 이것에 대한 항목이 있습니다 (소유해야 함).


답변

잠시 검색 후 동적 바인딩 (DD) 및 정적 바인딩 (SB)에 대한 몇 가지 참고 사항 :

1. 타이밍 실행 : (참고 1)

  • DB : 런타임에
  • SB : 컴파일러 시간

2. 사용 :

  • DB : 재정의
  • SB : 과부하 (정적, 개인, 최종) (참고 2)

참고:

  1. 사용하는 방법을 선호하는 평균 해석기 실행
  2. 수정 자 static, private 또는 final로 메서드를 재정의 할 수 없기 때문에
  3. http://javarevisited.blogspot.com/2012/03/what-is-static-and-dynamic-binding-in.html