質問: new ArrayList(); のように書くことが推奨されています。 一方、型安全はこれとは逆に変数には可能な限り厳密な型を指定しようという考え方です。 public void prints(String a, int b){}; のようなメソッドを public void prints(Object a, Object b){}; のように書いてしまうと、 prints("ああ",3); と呼ばなければならないところを prints(3,"ああ"); と書いてしまった時にコンパイルエラーが出ず、実行する時にエラーが出るようになり、望ましくありません。 上記の例を見ていると、多態性は曖昧に捉えることでメリットを得ているのに対し、型安全では厳密に捉えた方が良いとということがわかります。 この二つって相容れないものだと思うのですが、実際どうなんでしょうか。 これに対する皆さんの考えを教えてください。 補足 なんだか意図がうまく伝わっていない気がしたので、補足しておきます。 型安全は型によって、安全を担保しようという考え方で、enumやジェネリクスもこれに当たります。 enumによって、ある型に入れることのできる候補というのは制限することができますし、ジェネリクスも同様で、ArrayListだとString型しか入りません。 一方、多態性はアップキャストすることによるメリットを享受しようという考えに基づいています。 まとめると、多態性は曖昧に捉えており、型安全は厳密に範囲を狭めているということになるわけですが、これらのコンセプトって相容れないものだと思うのです。 しかしながら、現実のプログラミングにおいて、この二つの考えって共存しているとは思うのですが、一つの部分(ステートよりもさらに小さな単位で、例えば評価式)に着目してみると、どちらかしか使ってないと思うのです。 つまり、多態性と型安全の両方のコンセプトを一つ部分に取り込むことはできないと思うのですが、その使い分けについて、教えてくださいというのが私の質問だったのです。 書き方が悪かったですね。 申し訳ないです。
回答: > > 相反する(あるいは相反し得る)考えだと思ったのですが、皆様はどのようにお考えなのかと思い、質問させていただきます。 > > まとめると、多態性は曖昧に捉えており、型安全は厳密に範囲を狭めているということになるわけですが、これらのコンセプトって相容れないものだと思うのです。 > > > 「型安全性」と「多態性」は同じ評価軸上にある対立概念ではなく、それぞれが独立した概念であり、むしろ相補的なものと解釈しています。 > > 多態性は変数やインスタンスを曖昧に捉えることで、メリットを得ようとする考え方です。 > > 一方、多態性はアップキャストすることによるメリットを享受しようという考えに基づいています。 > > > 私とは解釈が異なります。Javaの文脈で多態性(Polymorphism)という場合、クラス継承やインターフェイス実装によるサブタイピングを意味するかと思います。ここでは、変数やインスタンスを「曖昧に捉える」ことが本質ではなく、変数やインスタンス間での「共通仕様を型(親クラス/インターフェイス)として表現する」ことです。共通の仕様を“型”によって表現することで、(コンパイル時に)無関係な型を渡せない安全なAPI仕様を設計できます。このように、型安全性と多態性は両立しえます。 逆に、(コンパイル時の)型安全性が無い多態性も実現可能です。Java言語ではリフレクション(Reflection)を使えば、任意の型に対して特定のメソッド呼び出しを試行できますが、コンパイル時には一切の型システムからのサポート(型検査)を受けられません。もちろん実行時には例外が送出さるのですがが、ここではコンパイル時の型安全性を損ねた多態性が実現されています。 > > 一方、型安全はこれとは逆に変数には可能な限り厳密な型を指定しようという考え方です。 > > > こちらも解釈が異なります。型安全(type safe)という単語はその文脈によって解釈の幅が広いようですが、型システムの原義では、型安全=「型システムによりプログラムが異常状態に陥らないことを保証する」という意味合いのようです。ここで言う“異常状態”は、対象とするプログラミング言語やシステムにより異なっており、Java言語それ自体では「未定義動作により任意のメモリ領域をアクセスしないところ」までを保証します。一方で、実行時に型に関する例外(例:ClassCastException, NullPointerException)が発生しないという型安全性はJava言語それ自身では達成できず、ライブラリのインターフェイス(API)設計の範疇となります。 > > 型安全は型によって、安全を担保しようという考え方で、enumやジェネリクスもこれに当たります。 > > enumによって、ある型に入れることのできる候補というのは制限することができますし、ジェネリクスも同様で、ArrayListだとString型しか入りません。 > > > ご指摘の通りenumやジェネリクスは、型に関する例外送出やプログラム不整合を避けるために、コンパイル時の型検査が可能な仕組みとして導入されました。 * enumは相関の無い列挙値を、型として区別できるようにする仕組みです。型安全性が向上します。(多態性には直接寄与しませんが、関数オーバーロードによるアドホック多相と組み合わせることはできます。) * ジェネリクスにより、共通の振る舞いを持つが型の一部が異なるという仕様を、コンパイル時に型検査できるようになります。ここでも多態性(正確にはパラメトリック多相)と型安全性が両立しています。