タイプを推測することによる自動ダウンキャスト


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が型推論を行わないという事実を除いて、この要件には何らかの理由がありますか?

新しい言語で自動ダウンキャストを実装する際の「落とし穴」はありますか?

例えば:

Apple child = parent; // no cast required 

ダウンキャストされるオブジェクトが常に正しいタイプであるとコンパイラが推測できる場合、ダウンキャストを明示的に表現できないはずですか?しかし、コンパイラのバージョンとそれによって実行できる型推論の量によっては、一部のプログラムが無効になる場合があります...型推論のバグにより、有効なプログラムの作成が妨げられる可能性があります...特別なものを導入しても意味がありません多くの要因に依存する場合、リリースごとに理由もなくキャストを追加または削除し続ける必要がある場合、ユーザーにとって直感的ではありません。
Bakuriu 2016年

回答:


24

アップキャストは常に成功します。

オブジェクトのランタイムタイプがキャストで使用されているタイプのサブタイプではない場合、ダウンキャストによってランタイムエラーが発生する可能性があります。

2番目は危険な操作であるため、ほとんどの型付きプログラミング言語では、プログラマが明示的に要求する必要があります。本質的に、プログラマーはコンパイラーに「私を信頼してください、私はよく知っています-これは実行時に問題ありません」と言っています。

型システムが関係している場合、アップキャストはコンパイラに証明の負担をかけます(静的にチェックする必要があります)。

適切に設計されたプログラミング言語は、ダウンキャストを完全に禁止するか、またはオプションの型を返すなど、安全なキャストの代替を提供すると主張することができますOption<T>。ただし、広く普及している言語の多くは、単純にTエラーを返し、それ以外の場合はエラーを発生させるという、よりシンプルで実用的なアプローチを選択しています。

特定の例では、コンパイラーは、単純な静的分析を介してparent実際にそれを推定しApple、暗黙のキャストを許可するように設計されている可能性があります。ただし、一般に問題は特定できないため、コンパイラがあまりにも多くの魔法を実行することは期待できません。


1
参考までに、オプションにダウンキャストする言語の例はRustですが、それは、それが真の継承を持たず、「任意の型」のみであるためです。
Kroltan 2016年

3

通常、ダウンキャストは、コンパイラが何かのタイプについて静的に知っている知識が、あなたが知っている(または少なくとも望んでいる)よりも具体的でない場合に行うことです。

あなたの例のような状況では、オブジェクトはとして作成され、Appleその参照はtypeの変数に参照を格納することによって破棄されましたFruit。次に、同じリファレンスをAppleもう一度使用したいとします。

情報は「ローカル」にのみ破棄されたため、宣言された型がであっても、コンパイラーparentは本当にである知識を維持できます。AppleFruit

しかし、通常は誰もそれをしません。を作成しAppleてそれをとして使用するApple場合は、Apple変数ではなく変数に格納しますFruit

あなたが持っている場合Fruitとして、それを使用したいApple、それは通常、あなたが取得した意味Fruit何らかの手段を通じて一般の任意の種類を返すことができますFruitが、この場合には、あなたがそれを知っていましたApple。ほとんどの場合、それを作成しただけでなく、他のコードによって渡されています。

明らかな例は、parseFruit「apple」、「orange」、「lemon」などの文字列を適切なサブクラスに変換できる関数がある場合です。一般的に、すべての私たち(とコンパイラ)は、この機能について知ることができ、それはいくつかの種類を返すということですFruitが、私が呼び出した場合はparseFruit("apple")、その後、私はそれを呼び出すために起こっているのを知っているAppleし、使用することをお勧めしますApple私はダウンキャストできるよう、方法を。

繰り返しになりますが、十分にスマートなコンパイラであれparseFruitば、定数でそれを呼び出しているため(Javaのように別のモジュールにあり、別のコンパイルがある場合を除いて)、のソースコードをインライン化することでこれを理解できます。しかし、動的情報を含むより複雑な例が、コンパイラーが検証するのがいかに困難になるか(あるいは不可能になるか)を簡単に確認できるはずです。

実際のコードではダウンキャストは通常、ダウンキャストが安全であるとジェネリックメソッドを使用して検証できなかった場合に発生します。アップキャストの直後にダウンキャストによって取得しようとしている同じタイプの情報を破棄するような単純なケースでは発生しません。


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; 
}

私たちはまだ元気です。静的解析ははるかに困難ですが、それでも可能です。

しかし、問題は誰かが追加した2番目の瞬間に現れます:

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

では、どこでエラーを発生させますか?これはジレンマを引き起こします。問題はにあると言えますが、Apple child = parent;これは「以前は機能していました」と反論することができます。一方、追加 eat(trouble);によって問題が発生しました」が、ポリモーフィズムの核心は、それを可能にすることです。

この場合、プログラマーの仕事の一部を行うことができますが、それを完全に行うことはできません。あきらめる前にそれをさらに進めるほど、何がうまくいかなかったかを説明することが難しくなります。したがって、エラーを早期に報告するという原則に従って、できるだけ早く停止することをお勧めします。

ところで、Javaでは、あなたが説明したダウンキャストは実際にはダウンキャストではありません。これは一般的なキャストで、リンゴをオレンジにキャストすることもできます。つまり、技術的に言えば、@ chiのアイデアはすでにここにあります。Javaにはダウンキャストはなく、「エブリキャスト」だけです。結果の型が引数の型の下流で見つからない場合にコンパイルエラーをスローする、特殊なダウンキャストオペレーターを設計することは理にかなっています。プログラマーが非常に正当な理由なしにそれを使用しないようにするために、「エブリキャスト」の使用をはるかに困難にすることは良い考えです。C ++のXXXXX_cast<type>(argument)構文が思い浮かびます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.