K.Maebashi's BBS

ご自由に書き込んでください。雑談も可。
テスト書き込みの類はテスト用掲示板にどうぞ

[日付順表示] [日付順インデックス] [スレッド順インデックス]

新規投稿 | 開設者ホームページへ戻る | ヘルプ

[1897] オーバーライド時の共変、反変について
投稿者:lang
2015/02/21 14:30:58

”プログラミング言語を作る”内の8-3-5章にて 下の解説を読みました。 class ShapeArray{ Shape get(int index); void set(int index, Shape shape); }; class CircleArray extends ShapeArray{ Circle get(int index); ・・・① void set(int index, Circle circle); ・・・② }; ①は戻りの値の型が共変なので問題無し。 ②は引数の方が共変であるため実行時チェックが必要。 アップキャストは常に可能ですが、 ダウンキャスト時は実行時チェックが必要だと理解しています。 戻りの値の型が共変、引数の型が共変、 ともにダウンキャストとなる、という認識ですが、 なぜ、問題の有無の判断が変わってくるのでしょうか。 一般的な言語のオーバーライドの動作に関することで 本掲示板への質問が適切かどうか分かりませんが、 もしよろしければ細くいただけると助かります。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1899] Re:オーバーライド時の共変、反変について
投稿者:(ぱ)こと管理人
2015/02/24 02:39:59

はじめまして。最近この掲示板もすっかり閑散としてしまっていて、 投稿に気付くのが遅れてしまいました。すみません。 ご質問いただきありがとうございます。 引用の順番を変更しますが、結論から言えば、 >戻りの値の型が共変、引数の型が共変、 >ともにダウンキャストとなる、という認識ですが、 >なぜ、問題の有無の判断が変わってくるのでしょうか。 引数の型が共変なのはダウンキャストですが、 戻り値の型が共変なのはアップキャストだからです。 >”プログラミング言語を作る”内の8-3-5章にて >下の解説を読みました。 p.289のコードですね。 >class ShapeArray{ > Shape get(int index); > void set(int index, Shape shape); >}; > >class CircleArray extends ShapeArray{ > Circle get(int index); ・・・① > void set(int index, Circle circle); ・・・② >}; まず、このコードは、 p.249の補足で、JavaのArrayStoreExceptionの説明をしていますが、 JavaがArrayStoreExceptionを発生させるという実行時チェックを しなければならなくなった、ということについて、共変の考え方からも 説明できる、ということを示すためのコードです。 ここまではよいでしょうか。 p.249の補足のサンプルコードで、 1: Line[] lines = new Line[10]; 2: Shape[] shapes = lines; 3: shapes[3] = new Circle(); というコードがあったとき、Javaでは3行目でArrayStoreExceptionが 発生します。shapesはあくまでLineの配列であり、Circleの配列では ないからです。 これはJavaにおける配列の話ですが、Javaの配列をShapeArrayとか CircleArrayといったクラスで表現すると、p.289のコードのようになります。 ここで、CircleArrayのインスタンスcircleArrayがあったとして、 Shape shape = circleArray.get(i); というコードは、アップキャストなので合法ですが、 circleArray.set(i, shape); というコードは、ダウンキャストになります。CircleArray.set()の第2引数の 型はCircleなので、もしShapeを渡したければダウンキャストが必要になります。 Javaでは、このダウンキャストに相当する実行時例外が、ArrayStoreExceptionに なっている、と言えるでしょう。 この例ではCircleArrayがShapeArrayを継承しているので、もし引数の共変を 許せば、 ShapeArray shapes = circleArray; shapes.set(i, shape); と書けてしまいます。このshapes.set(i, shape);が、 まさにp.249のコードの shapes[i] = new Circle(); に相当するわけです。 これで回答になっているでしょうか?
[この投稿を含むスレッドを表示] [この投稿を削除]
[1901] Re:オーバーライド時の共変、反変について
投稿者:lang
2015/02/24 22:31:14

ご丁寧にご回答ありがとうございました。 理解できました。 確かに、引数時はダウンキャストになりますね。 Javaだとこの場合ははオーバライドにはならないで、オーバーロード、 引数型が違う同名の別メソッドが増えただけと見なすことでガードするという実装なんですね。 前橋さんの書籍で共変、反変という言葉を知ったのですが、 非常に一般的な概念のようでした。 直接Diksamとはご関係のない質問になってしまい大変恐縮です。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1903] Re:オーバーライド時の共変、反変について
投稿者:(ぱ)こと管理人
2015/02/25 03:35:46

>Javaだとこの場合ははオーバライドにはならないで、オーバーロード、 >引数型が違う同名の別メソッドが増えただけと見なすことでガードするという実装なんですね。 こう言っては何なのですが、たぶんこれはJavaを「買いかぶりすぎ」だと思うのです。 引数については、反変はOK、共変はNG、 戻り値については、共変はNG、反変はOK なのですが、 JDK1.4までのJavaでは、 引数については、型が違えばオーバーロード、 戻り値については、型が違えばコンパイルエラー でした。戻り値については共変は許されるべきなのに、古いJavaではコンパイルエラーに してしまっていたのです。 Javaでは、JDK1.5(Tiger)において、「共変戻り値」が許されるようになりました。 後になってこれを許すようになったということは、 当初は、共変とか反変とかそんなことはJavaの作者はあんまりまじめに考えていなくて、 後になって、共変戻り値はOKにすることができたけれど、反変引数は、既にそれを オーバーロードという機能で使ってしまっていたためにOKにできなかったのではないか、 と思います。 メソッドオーバーロードがすごく便利だから反変引数を捨ててでもそちらを取った、 というのは言語設計者の選択肢として理解できますが(私はオーバーロードが便利だと 思わないのでその選択肢は取りませんが)、 本来使えるべき共変戻り値を当初エラーにしてしまっていた(しかも1.5でそれを 撤回した)というのは、あまりこう、ちゃんと考えた結果には見えないんですよねえ。
[この投稿を含むスレッドを表示] [この投稿を削除]