유형을 유추하여 자동 다운 캐스팅


11

Java에서는 변수를 다운 캐스트하기 위해 명시 적으로 캐스트해야합니다.

public class Fruit{}  // parent class
public class Apple extends Fruit{}  // child class

public static void main(String args[]) {
    // An implicit upcast
    Fruit parent = new Apple();
    // An explicit downcast to Apple
    Apple child = (Apple)parent;
}

java가 형식 유추를 수행하지 않는다는 사실 외에도이 요구 사항에 대한 이유가 있습니까?

새로운 언어로 자동 다운 캐스팅을 구현할 수있는 "gotchas"가 있습니까?

예를 들어 :

Apple child = parent; // no cast required 

컴파일러가 다운 캐스팅되는 객체가 항상 올바른 유형임을 유추 할 수 있다면 다운 캐스트를 명시 적으로 표현할 수 없어야합니까? 그러나 컴파일러 버전과 얼마나 많은 유형의 유추에 따라 일부 프로그램이 유효하지 않을 수 있습니다 ... 유형 유추 기의 버그로 인해 유효한 프로그램이 작성되는 것을 막을 수 있습니다 ... 특별히 소개하는 것이 합리적이지 않습니다. 많은 요소에 의존하는 경우, 모든 릴리즈에서 이유없이 캐스트를 계속 추가하거나 제거해야하는 사용자에게는 직관적이지 않습니다.
Bakuriu

답변:


24

업 캐스트는 항상 성공합니다.

오브젝트 런타임 유형이 캐스트에 사용 된 유형의 하위 유형이 아닌 경우 다운 캐스트로 인해 런타임 오류가 발생할 수 있습니다.

두 번째는 위험한 작업이므로 대부분의 유형이 지정된 프로그래밍 언어에서는 프로그래머가 명시 적으로 요청해야합니다. 기본적으로 프로그래머는 컴파일러에게 "믿어주세요. 더 잘 알고 있습니다. 런타임에 괜찮을 것입니다"라고 말하고 있습니다.

타입 시스템과 관련하여, 업 캐스트는 증명에 대한 부담을 컴파일러에 정적으로 (정적으로 확인해야 함), 다운 캐스트는 증명에 대한 부담을 프로그래머 (고려해야 함)에 부과합니다.

적절하게 설계된 프로그래밍 언어는 다운 캐스트를 완전히 금지하거나 안전한 캐스트 대안 (예 : 선택적 유형 반환)을 제공한다고 주장 할 수 있습니다 Option<T>. 그러나 많은 널리 퍼진 언어는 단순히 T다른 방법으로 오류 를 반환 하고 발생시키는 더 단순하고 실용적인 접근 방식을 선택했습니다 .

특정 예에서, 컴파일러 parent는 실제로 Apple간단한 정적 분석을 통한 것으로 추론 하고 암시 적 캐스트를 허용 하도록 설계되었을 수 있습니다 . 그러나 일반적으로 문제를 결정할 수 없으므로 컴파일러가 너무 많은 마술을 수행 할 것으로 기대할 수 없습니다.


1
참고로, 선택 사항으로 다운 캐스트하는 언어의 예는 Rust이지만, 이는 진정한 상속이없고 "any type" 만 있기 때문 입니다.
Kroltan

3

일반적으로 다운 캐스팅은 컴파일러가 알고있는 것의 유형에 대해 정적으로 알려진 지식이 알고있는 것 (또는 적어도 희망)보다 덜 구체적 일 때 수행하는 작업입니다.

귀하의 예와 같은 상황에서 객체는로 생성 된 Apple다음 참조 유형의 변수에 참조를 저장하여 지식이 버려졌습니다 Fruit. 그런 다음 Apple다시 같은 참조를 사용하려고 합니다.

정보는 "로컬로"버려 졌기 때문에, 선언 된 유형이이지만 컴파일러 parent는 실제로 지식을 유지할 수 있습니다.AppleFruit

그러나 보통 아무도 그렇게하지 않습니다. 를 만들고 Apple그것을로 사용 Apple하려면 Apple변수가 아닌 변수에 저장하십시오 Fruit.

당신이 Fruit그것을 가지고 있고 그것을 사용하고자 할 Apple때 일반적으로 당신 Fruit일반적으로 모든 종류를 반환 할 수있는 몇 가지 수단을 통해 얻은 것을 의미 Fruit하지만,이 경우 당신은 그것을 알고 있습니다 Apple. 거의 항상 당신은 단지 그것을 구성한 것이 아니라 다른 코드에 의해 전달되었습니다.

parseFruit"apple", "orange", "lemon"등과 같은 문자열을 적절한 서브 클래스로 바꿀 수 있는 함수 가 있다면 분명한 예입니다 . 일반적으로 이 모든 기능은 그것이 어떤 종류의 반환이다 우리 (컴파일러)를 알 수 있습니다 Fruit,하지만 난 호출하는 경우 parseFruit("apple")다음 의는 전화를가는 것을 알고 Apple및 사용할 수도 있습니다 Apple내가 다운 캐스트 할 수 있도록, 방법.

다시 한 번 충분히 호출하면 (다른 모듈에 있고 Java와 같이 별도의 컴파일이없는 한) 소스 코드를 인라인하여 충분히 똑똑한 컴파일러 여기에서 알아낼 parseFruit 있습니다. 그러나 동적 정보를 포함하는 더 복잡한 예제가 컴파일러가 확인하기 어려워지는 (또는 불가능한) 방법을 쉽게 알 수 있어야합니다.

실제 코드 다운 캐스트는 일반적으로 컴파일러가 일반적인 방법을 사용하여 다운 캐스트가 안전하다는 것을 확인할 수없는 경우에 발생합니다. 다운 캐스트를 통해 다시 얻으려고하는 것과 동일한 유형 정보를 버리는 업 캐스트 직후와 같은 단순한 경우는 아닙니다.


3

어디에서 선을 그리려고 하는가의 문제입니다. 암시 적 다운 캐스트의 유효성을 감지하는 언어를 설계 할 수 있습니다.

public static void main(String args[]) { 
    // An implicit upcast 
    Fruit parent = new Apple();
    // An implicit downcast to Apple 
    Apple child = parent; 
}

이제 메소드를 추출해 봅시다 :

public static void main(String args[]) { 
    // An implicit upcast 
    Fruit parent = new Apple();
    eat(parent);
}
public static void eat(Fruit parent) { 
    // An implicit downcast to Apple 
    Apple child = parent; 
}

우리는 여전히 좋다. 정적 분석은 훨씬 어렵지만 여전히 가능합니다.

그러나 누군가가 추가하는 두 번째 문제는 다음과 같습니다.

public static void causeTrouble() { 
    // An implicit upcast 
    Fruit trouble = new Orange();
    eat(trouble);
}

이제 어디에서 오류를 발생 시키겠습니까? 이로 인해 딜레마 Apple child = parent;가 생겨서 문제가 있다고 말할 수 있지만 "하지만 이전에는 효과가있었습니다"라는 반박이있을 수 있습니다. 다른 한편으로, 추가 eat(trouble);는 문제를 일으켰다 ", 그러나 다형성의 요점은 정확히 그것을 허용하는 것입니다.

이 경우 프로그래머의 일부 작업을 수행 할 수 있지만 모든 작업을 수행 할 수는 없습니다. 포기하기 전에 더 많이 가져 갈수록 무엇이 잘못되었는지 설명하기가 더 어려워집니다. 따라서 오류를 조기에보고하는 원칙에 따라 가능한 빨리 중지하는 것이 좋습니다.

BTW, Java에서 설명 한 다운 캐스트는 실제로 다운 캐스트가 아닙니다. 이것은 일반적인 캐스트이며 사과를 오렌지에도 캐스트 할 수 있습니다. 기술적으로 @chi의 아이디어는 이미 여기에 있습니다. Java에는 다운 캐스트가 없으며 "모든 캐스트"만 있습니다. 결과 유형이 인수 유형에서 다운 스트림으로 찾을 수없는 경우 컴파일 오류를 발생시키는 특수 다운 캐스트 연산자를 설계하는 것이 합리적입니다. 프로그래머가 별다른 이유없이 사용하지 못하도록 "모든 캐스트"를 사용하기 훨씬 더 어렵게 만드는 것이 좋습니다. C ++의 XXXXX_cast<type>(argument)구문이 떠 오릅니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.