実際、あなたはその場でそれをかなりうまく言いました。
真実は、櫛の「インスタンス」はほとんどの場合悪い考えです (たとえば、マーシャリングまたはシリアル化を行っているとき、短い間隔ですべての型情報が手元にない場合に発生する例外です)。 、それ以外の場合、それは悪いクラス階層の兆候です。
それが悪い考えであることがわかる方法は、それがコードをもろくすることです: それを使用し、型の階層が変更された場合、そのインスタンスの櫛が発生するすべての場所で壊れる可能性があります。さらに、強力な型付けの利点が失われます。コンパイラは、事前にエラーをキャッチすることによってあなたを助けることはできません. (これは、C の型キャストによって引き起こされる問題にいくぶん類似しています。)
アップデート
コメントからは、私がはっきりしていないように見えるので、これを少し拡張させてください。C で型キャストを使用する理由、またはinstanceof
、「あたかも」と言いたいからです: これfoo
をbar
. さて、C では、実行時の型情報がまったくないため、ネットなしで作業しているだけです。何かを型キャストすると、生成されたコードは、そのアドレスが何があっても特定の型を含んでいるかのように扱います。 、黙って何かを破壊するのではなく、実行時エラーが発生することを願うだけです。
ダックタイピングはそれを標準に引き上げるだけです。Ruby、Python、または Smalltalk のような動的で弱い型付けの言語では、すべてが型付けされていない参照です。実行時にメッセージを送信し、何が起こるかを確認します。特定のメッセージを理解すると、「アヒルのように歩き」、それを処理します。
これは非常に便利で便利です。Python でジェネレータ式を変数に代入したり、Smalltalk でブロックを変数に代入したりするなどの素晴らしいハックが可能になるからです。ただし、厳密に型指定された言語がコンパイル時にキャッチできる実行時のエラーに対して脆弱であることを意味します。
Java のような厳密に型指定された言語では、ダック タイピングをまったく厳密に行うことはできません。何かをどの型として扱うかをコンパイラに伝える必要があります。型キャストを使用してダックタイピングのようなものを取得できるため、次のようなことができます
Object x; // A reference to an Object, analogous to a void * in C
// Some code that assigns something to x
((FoodDispenser)x).dropPellet(); // [1]
// Some more code
((MissleController)x).launchAt("Moon"); // [2]
FoodDispenser
実行時には、x がat [1] またはMissleController
at [2]の一種である限り問題ありません。そうでなければブーム。というか案外ノーブーム。
あなたの説明の中で、あなたはelse if
とのくしを使って身を守りますinstanceof
Object x ;
// code code code
if(x instanceof FoodDispenser)
((FoodDispenser)x).dropPellet();
else if (x instanceof MissleController )
((MissleController)x).launchAt("Moon");
else if ( /* something else...*/ ) // ...
else // error
これで実行時エラーから保護されましたが、後で適切な処理を行う責任がありelse
ます。
しかしここで、'x' が 'FloorWax' と 'DessertTopping' の型を取ることができるように、コードを変更したとします。ここで、すべてのコードを調べて、そのコームのすべてのインスタンスを見つけて変更する必要があります。現在、コードは「脆弱」です。要件の変更は、多くのコードの変更を意味します。OO では、コードの脆さを軽減しようと努力しています。
OO の解決策は、代わりにポリモーフィズムを使用することです。これは一種の制限されたダック タイピングと考えることができます。何かが信頼して実行できるすべての操作を定義しています。これを行うには、下位クラスのすべてのメソッドを持つ上位クラス (おそらく抽象クラス) を定義します。Java では、このようなクラスは「インターフェース」と表現するのが最適ですが、クラスのすべてのタイプ プロパティを備えています。実際、インターフェイスは、特定のクラスが別のクラスであるかのように動作することを信頼できるという約束であると見なすことができます。
public interface VeebleFeetzer { /* ... */ };
public class FoodDispenser implements VeebleFeetzer { /* ... */ }
public class MissleController implements VeebleFeetzer { /* ... */ }
public class FloorWax implements VeebleFeetzer { /* ... */ }
public class DessertTopping implements VeebleFeetzer { /* ... */ }
あとは、VeebleFeetzer への参照を使用するだけで、コンパイラがそれを計算してくれます。VeebleFeetzer のサブタイプである別のクラスをたまたま追加した場合、コンパイラはメソッドを選択し、掘り出し物の引数をチェックします。
VeebleFeetzer x; // A reference to anything
// that implements VeebleFeetzer
// Some code that assigns something to x
x.dropPellet();
// Some more code
x.launchAt("Moon");