2013-08-09 1 views
7

이 질문이 주로 의견을 바탕으로 한 것이라면 용서해주십시오.하지만 필자는 그것이 선택되지 않았으며 선택의 타당한 이유가 있음을 인정합니다. 여기 예제가 있습니다. 죄송합니다, 정말 긴하지만, 매우 간단합니다오버라이드와 오버로딩에 대한 컴파일러 해석

인터페이스 :

public interface Shape 
{ 
    double area(); 
} 

구현 클래스 1 :

import static java.lang.Math.PI; 

public class Circle implements Shape 
{ 
    private double radius; 

    public Circle(double radius) 
    { 
     this.radius = radius; 
    } 

    public double area() 
    { 
     return PI*radius*radius; 
    } 
} 

구현 클래스 2 :

public class Square implements Shape 
{ 
    private double size; 

    public Square(double sideLength) 
    { 
     size = sideLength; 
    } 

    public double area() 
    { 
     return size*size; 
    } 
} 

드라이버 :

Shape[] shapes = new Shape[]{new Circle (5.3), new Square (2.4)}; 

System.out.println(shapes[0].area()); //prints 88.247... 
System.out.println(shapes[1].area()); //prints 5.76 

.area()CircleSquare으로 바뀌었기 때문에 효과가 있습니다. 이제 내 질문이 진정으로 시작됩니다. 의 드라이버가 이러한 방법을 가지고 있다고 가정 해 봅시다 :

public static void whatIs(Shape s) 
{ 
    System.out.println("Shape"); 
} 

public static void whatIs(Circle s) 
{ 
    System.out.println("Circle"); 
} 

public static void whatIs(Square s) 
{ 
    System.out.println("Square"); 
} 

을 우리가 호출하는 경우 : 자바 CircleSquareShape들로 개체를 해석하지 때문에

whatIs(shapes[0]); //prints "Shape" 
whatIs(shapes[1]); //prints "Shape" 

이 발생합니다. 이제 우리는 내 질문은 배경을 가지고

if (shapes[0] instanceof Circle) 
{ 
    whatIs((Circle) shapes[0]); //prints "Circle" 
} 
if (shapes[1] instanceof Square) 
{ 
    whatIs((Square) shapes[1]); //prints "Square" 
} 

: 물론 우리는을 통해 원하는 결과를 얻을 수 있습니다? "모양"등이 whatIs(shapes[0]);이 인쇄됩니다 컴파일러/언어 설계에 기여 어떤 이유
에서와 같이 Java 컴파일러는 왜 오버로드 된 메소드가 아닌 관련 객체에 대해 오버라이드 된 메소드를 정확하게 구별 할 수 있습니까? 보다 구체적으로, 운전자가 접근에있는 유일한 방법은 경우 :

public static void whatIs(Circle s) 
{ 
    System.out.println("Circle"); 
} 
public static void whatIs(Square s) 
{ 
    System.out.println("Square"); 
} 

우리가 나타내는 우리가 두 가지 오류 (Square에 대한 하나 Circle 하나)를 얻을 것이다

whatIs(shapes[0]); 
whatIs(shapes[1]); 

호출을 시도 그 :

  • 방법 Driver.whatIs (광장)이 적용되지 않습니다
    • 실제 인수 모양은

그래서 다시, 지금 우리가 핵심적 껄끄 러운, 자바와 같은 상황을 처리 할 수없는 이유를 늘어 놓던 것을 메소드 호출 변환에 의해 광장으로 변환 할 수 없습니다 이? 마찬가지로, 이것은 효율성에 대한 우려로 이루어지며, 일부 설계 결정으로 인해 가능하지 않을 수 있으며, 이는 어떤 이유로 든 나쁜 관행입니까?

+0

또한 누구나 제목에 대해 더 좋은 아이디어가 있다면 제안을하거나 편집 해주세요. 아마 가장 좋은 것은 아니지만 더 나은 것을 생각할 수는 없습니다. 이 게시물을 통해 모든 사람들에게 감사드립니다! –

+4

[JLS 섹션 15.2.2] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.2) (오버로드) 및 [JLS 섹션 8.4.8.1] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.8.1) (재정의) –

+1

재미있는 질문 및 @ RohitJain이 의견에 감사 드리며, 나는 그것들을 또한 읽어야 할 것입니다. –

답변

4

왜 Java 컴파일러는 관련 개체에 대해 오버라이드 된 메서드를 정확하게 구별 할 수 있지만 오버로드 된 메서드는 구별하지 못합니까?

수 없습니다.

엄격하게 유형을 확인하면 &을 확인할 수 있습니다. 코드가 shapes[0].area()이면 area 메소드가 Shape이고 "해당 객체의 영역 (call)"으로 컴파일됩니다. 실행시에 존재하는 구상 오브젝트에는, 그 메소드가 반드시 보증되고 있습니다. 실제로 사용되는 클래스의 버전은 런타임에 동적으로 결정됩니다.

오버로드 된 메서드 호출은 동일하게 작동합니다. 컴파일러는 Shape을보고이를 "기본 셰이프 버전의 whatis()"로 컴파일합니다. 그것을 바꾸고 싶다면 (심지어 기본 버전이 없다는 것을 허용한다면) 컴파일 할 때 타입을 결정할 수 있어야합니다.

하지만 오브젝트가 그 시점에서 런타임에 가질 유형을 결정할 수있는 컴파일러를 만드는 것은 불가능합니다. 예를 들어 생각해보십시오.

final Shape[] shapes = new Shape[] { new Circle(5.3), new Square(2.4) }; 
    new Thread() { 
     public void run() { 
      shapes[0] = new Square(1.5); 
     } 
    }.start(); 
    whatIs(shapes[0]); 

해당 코드를 실행해야 찾아 낼 수 있습니다. 당신은 런타임에 동적 메소드 호출을 달성하기 위해

컴파일러는 자동

if (shapes[0] instanceof Circle) 
{ 
    whatIs((Circle) shapes[0]); //prints "Circle" 
} 

과 같은 코드를 생성 할 수 있지만 그렇지 않습니다. 나는 이유를 모르지만 때로는 깔끔할 것입니다. instanceof은 종종 나쁜 수업 설계의 신호가됩니다. 외부에서 바라본 차이를 보지 말고, 수업이 다르게 행동하도록하여 외부가 알 필요가 없도록하십시오.

+0

필자의 궁금증은 필자의 질문과 같다. 왜 컴파일러가 마지막 예제에서 코드를 자동 생성하지 않는가? 그럼에도 불구하고 내 원래의 질문에 대답했습니다. 고맙습니다. –

2

whatIs 메서드 중 하나에 대한 디스패치는 컴파일 타임에 컴파일러에 의해 결정됩니다. area 메서드 중 하나에 대한 호출은 참조 할 개체의 실제 클래스를 기반으로 런타임에 결정됩니다.

5

객체 지향 기능을 지원하는 Java는 다형성을 지원하므로 area을 호출하면 특정 인스턴스의 메소드가 무엇이든간에 area이 호출됩니다. 이것은 런타임에 결정됩니다.

그러나이 다형성은 오버로드 된 메서드에서 지원되지 않습니다. Java Language Specification, Section 8.4.9이 커버 :

메소드가 불려 가면 (§15.12), 실제 인수 (그리고 명시적인 형태 인수)와 인수의 컴파일 타임 유형의 수는 컴파일시에 사용된다 , 호출 될 메소드의 서명을 결정한다 (15.12.2 절). 이 될 메서드가 인스턴스 메서드 인 경우 호출 할 실제 메서드는 동적 메서드 조회 (15.12.4)를 사용하여 런타임에 으로 결정됩니다.

즉, 오버로드 된 메서드를 사용하면 메서드는 변수의 컴파일 시간 유형을 사용하여 컴파일 타임에 선택되며 런타임에는 다형성과 같이 사용하지 않습니다.

1

Q : 왜 Java 컴파일러는 관련 객체에 대한 오버라이드 된 메소드를 정확하게 구별 할 수 있지만 오버로드 된 메소드는 식별 할 수 없습니다 ... 왜 Java는 이와 같은 상황을 처리하지 못합니까?

대답 : 질문이 거꾸로 들었습니다.

Java ALLOWS "오버로드"와 "오버라이드"를 구별 할 수 있습니다.

을 의미하는 것으로 추측하지 않으므로 둘 중 하나를 선택해야합니다.

1

음, 바보 같은 대답, 당신은 (어떤 종류의 검사없이) 잘

class Shape{ 
    public abstract String whatIs(); 
} 

class Square{ 
    public String whatIs(){ return "Square"; } 
} 
class Circle{ 
    public String whatIs(){ return "Circle"; } 
} 

을 이러한 방식으로 작동 그리고이

Shape square = new Square(); 
Shape circle = new Circle(); 

System.out.println(square.whatIs()) //prints 'square' 
System.out.println(circle.whatIs()) //prints 'circle 

전혀처럼 그들을 호출 할 whatis는 기능을 얻을 수 네가 묻는 질문에 대한 모든 대답은 ...하지만 나는 저항 할 수 없었다.

+0

당신은 이것이 내가 요구 한 것이 아니라는 점에서 맞습니다. 그럼에도 불구하고, 그것은 흥미 롭습니다. 그리고 그것이 누군가를 도울 것이라고 확신합니다. +1 –