質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

4回答

5173閲覧

javaの型安全と多態性について

退会済みユーザー

退会済みユーザー

総合スコア0

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

2グッド

2クリップ

投稿2016/08/06 05:42

編集2016/08/07 23:58

javaの型安全と多態性についての質問です。
勉強していて、相反する(あるいは相反し得る)考えだと思ったのですが、皆様はどのようにお考えなのかと思い、質問させていただきます。
多態性は変数やインスタンスを曖昧に捉えることで、メリットを得ようとする考え方です。
例えば、コレクションの中身を表示するようなメソッドがあったとして、

public void prints(List<String>) {};

public void prints(ArrayList<String>) {};

上ではArrayListもLinkedListでも同様に表示できますが、下ではArrayListしか表示させることができません。
また、コレクションを宣言するときは
List<String> names = new ArrayList<String>();
のように書くことが推奨されています。

一方、型安全はこれとは逆に変数には可能な限り厳密な型を指定しようという考え方です。

public void prints(String a, int b){};

のようなメソッドを

public void prints(Object a, Object b){};

のように書いてしまうと、
prints("ああ",3);
と呼ばなければならないところを
prints(3,"ああ");
と書いてしまった時にコンパイルエラーが出ず、実行する時にエラーが出るようになり、望ましくありません。

上記の例を見ていると、多態性は曖昧に捉えることでメリットを得ているのに対し、型安全では厳密に捉えた方が良いとということがわかります。
この二つって相容れないものだと思うのですが、実際どうなんでしょうか。

これに対する皆さんの考えを教えてください。

補足
なんだか意図がうまく伝わっていない気がしたので、補足しておきます。
型安全は型によって、安全を担保しようという考え方で、enumやジェネリクスもこれに当たります。
enumによって、ある型に入れることのできる候補というのは制限することができますし、ジェネリクスも同様で、ArrayList<String>だとString型しか入りません。
一方、多態性はアップキャストすることによるメリットを享受しようという考えに基づいています。
まとめると、多態性は曖昧に捉えており、型安全は厳密に範囲を狭めているということになるわけですが、これらのコンセプトって相容れないものだと思うのです。
しかしながら、現実のプログラミングにおいて、この二つの考えって共存しているとは思うのですが、一つの部分(ステートよりもさらに小さな単位で、例えば評価式)に着目してみると、どちらかしか使ってないと思うのです。
つまり、多態性と型安全の両方のコンセプトを一つ部分に取り込むことはできないと思うのですが、その使い分けについて、教えてくださいというのが私の質問だったのです。
書き方が悪かったですね。
申し訳ないです。

matobaa, yohhoy👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

argius

2016/08/07 14:55

ArrayList<String> names = new ArrayList<String>();と宣言してもprints(List<String>)は呼べるので、「したがって」の例として適切でないように思います。
退会済みユーザー

退会済みユーザー

2016/08/07 23:53

確かに、適当ではないですね。 ご指摘ありがとうございます。
iwamoto_takaaki

2016/08/08 01:24

> そうすると、「型を絞り込むことにより得られるメリット」と「型を曖昧にすることによりメリット」の両方が存在すると思うのですが、それらをどのように区別しているのか、というのが私の質問になります。 この点は、質問に追記すべきだと思います。ほかの方が検索するかもしれないからです。また、それにより、追加の回答が得られます。
guest

回答4

0

ベストアンサー

相反する(あるいは相反し得る)考えだと思ったのですが、皆様はどのようにお考えなのかと思い、質問させていただきます。
まとめると、多態性は曖昧に捉えており、型安全は厳密に範囲を狭めているということになるわけですが、これらのコンセプトって相容れないものだと思うのです。

「型安全性」と「多態性」は同じ評価軸上にある対立概念ではなく、それぞれが独立した概念であり、むしろ相補的なものと解釈しています。

多態性は変数やインスタンスを曖昧に捉えることで、メリットを得ようとする考え方です。
一方、多態性はアップキャストすることによるメリットを享受しようという考えに基づいています。

私とは解釈が異なります。Javaの文脈で多態性(Polymorphism)という場合、クラス継承やインターフェイス実装によるサブタイピングを意味するかと思います。ここでは、変数やインスタンスを「曖昧に捉える」ことが本質ではなく、変数やインスタンス間での「共通仕様を型(親クラス/インターフェイス)として表現する」ことです。共通の仕様を“型”によって表現することで、(コンパイル時に)無関係な型を渡せない安全なAPI仕様を設計できます。このように、型安全性と多態性は両立しえます。

逆に、(コンパイル時の)型安全性が無い多態性も実現可能です。Java言語ではリフレクション(Reflection)を使えば、任意の型に対して特定のメソッド呼び出しを試行できますが、コンパイル時には一切の型システムからのサポート(型検査)を受けられません。もちろん実行時には例外が送出さるのですがが、ここではコンパイル時の型安全性を損ねた多態性が実現されています。

一方、型安全はこれとは逆に変数には可能な限り厳密な型を指定しようという考え方です。

こちらも解釈が異なります。型安全(type safe)という単語はその文脈によって解釈の幅が広いようですが、型システムの原義では、型安全=「型システムによりプログラムが異常状態に陥らないことを保証する」という意味合いのようです。ここで言う“異常状態”は、対象とするプログラミング言語やシステムにより異なっており、Java言語それ自体では「未定義動作により任意のメモリ領域をアクセスしないところ」までを保証します。一方で、実行時に型に関する例外(例:ClassCastException, NullPointerException)が発生しないという型安全性はJava言語それ自身では達成できず、ライブラリのインターフェイス(API)設計の範疇となります。

型安全は型によって、安全を担保しようという考え方で、enumやジェネリクスもこれに当たります。
enumによって、ある型に入れることのできる候補というのは制限することができますし、ジェネリクスも同様で、ArrayList<String>だとString型しか入りません。

ご指摘の通りenumやジェネリクスは、型に関する例外送出やプログラム不整合を避けるために、コンパイル時の型検査が可能な仕組みとして導入されました。

  • enumは相関の無い列挙値を、型として区別できるようにする仕組みです。型安全性が向上します。(多態性には直接寄与しませんが、関数オーバーロードによるアドホック多相と組み合わせることはできます。)
  • ジェネリクスにより、共通の振る舞いを持つが型の一部が異なるという仕様を、コンパイル時に型検査できるようになります。ここでも多態性(正確にはパラメトリック多相)と型安全性が両立しています。

投稿2016/08/08 02:12

編集2016/08/08 02:42
yohhoy

総合スコア6189

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2016/08/08 02:41

なるほど、目からウロコですね。 多態性の本質は「共通仕様を型として表現する」ことであり、そのようにしておけば無関係なインスタンスや変数が入り込む余地はなく、型安全も両立している。 という解釈でよろしいでしょうか。 一方、「型安全」という言葉に対する解釈ですが、yohhoyさんは「型に関して、プログラムが異常な動作をしないようにする」という意味合いなので、私のいう「型を厳密に絞り込むこと」では意味が狭すぎるということでしょうか?
yohhoy

2016/08/08 03:37 編集

> 多態性の本質は「共通仕様を型として表現する」ことであり、そのようにしておけば無関係なインスタンスや変数が入り込む余地はなく、型安全も両立している。 おおむね「はい」。多態性にはいくつか構造的な種類が存在しており、サブタイピング多態(多相)の説明としては上記の通りです。 > 「型を厳密に絞り込むこと」では意味が狭すぎる 狭い/広いという程度問題ではなく、互いの論点が少しずれていると思います。billさんの主張は「型安全のためには型を具象化せよ」ですが、私の回答では「型システムを使って変なことを防ぐ=型安全」と言っているだけです。 追記:今回の回答にあたり http://togetter.com/li/376541 こちらの議論がためになりました。型安全性は、狭義には「型に"関する"安全性」を指しますが、型システムとして表現可能な概念であれば、必ずしも型自身の安全性に限る必要はありません。例えば、Rustというプログラミング言語では、型にデータの有効期間という追加情報が埋め込まれており、これにより「データ競合が生じないという安全性」を型システムを使って保証します。(私は詳しくないのですが)Haskell言語も強力な(≒表現力の高い)型システムにより、型に関すること以上の安全性を保証できる能力があります。
guest

0

型安全とは簡単にいうとキャストに失敗する恐れがないことです。

多態の時には基底クラス方向(objectクラス)にむかってキャストします。この方向であれば、コンパイラが型の安全性をチェックしてくれます。つまり型安全です。

型安全にならないのは、派生クラスにキャストする場合です。型安全にするにはこれ 避けましょうという話です。

ちなみにそれぞれ、基底クラス方向へのキャストをアップキャスト、派生クラス方向へのキャストをダウンキャストと呼びます。


補足を受けて追記します。

yohhoyさんの意見に全面的に賛成します。かなり詳しいです。
型安全と多態は別の概念だし、共存可能で、ぶつかるのは例外的だとおもいます。

型安全の話ついでに多態について。
アップキャストやジェネリックは方安全、つまり使ってもなんら問題は起こらないのでどんどん使うことをお勧めします。

型情報を厳密にすると、ほかの型でも同じようなメソッドを組むことになり、調査負荷の増大、メンテ漏れ、結果バグの発生といった問題(技術的負債)を抱えることになります。書いた時の必要とされていたかというのは気にせず、メソッドとして必要最小限な適切な型で受けるようにすることをお勧めします。

投稿2016/08/06 07:17

編集2016/08/08 02:32
iwamoto_takaaki

総合スコア2883

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2016/08/08 09:56

実用的なアドバイスありがとうございます。 「必要最小限な適切な型」を引数に設定しておけば、多態性のメリットも得られるし、型の安全性も担保されるわけですね。
iwamoto_takaaki

2016/08/08 15:13

そうです。 したがって、public void prints(Object a, Object b){}; の例は、内部でダウンキャストが必要なので不適切ということになります。 public void prints(List<String>) {}; の例では、問題なければこちらの方が良いということになります。 せっかく例があったのに言及を漏らしてました。
guest

0

  • ArrayListクラス

ensureCapacity()
removeRange()
trimToSize()

あたりのメソッドはListでは宣言されていません。
なのでこのメソッドが使えないというデメリットがあります。
ArrayList<E>
こういったメソッド一覧を用いてプログラムを書こうとしたときに、これらのメソッド呼び出しが失敗して困る原因になりかねません。

APIとして他人に提供するならListのほうがいいかもしれませんが、自分で作ったプログラムならArrayを消すだけで修正できるので最初からArrayListで宣言、利用してしまったほうがミスが少ないでしょう。

ただし、それぞれListのメソッド、Objectのメソッドしか利用しない場合はそのクラスの型として宣言したほうが多くを受け入れることができます。
メソッドの利用目的に合わせて、その中でできるだけ広い範囲をカバー(スーパークラスの型で受け取り)したほうがいいということでしょう。

  • まとめると

今回しか利用しないメソッド→その状況以外使用しないので厳密に
多くのクラスから利用するメソッド→広い範囲での利用を想定して曖昧?に
必ずスーパークラスのメソッドしか利用しないメソッド→スーパークラスの型を指定
スーパークラス以外のメソッドを利用する可能性があるメソッド→サブクラスの型を指定


蛇足ですが、
public void prints(String a, int b){};
public void prints(Object a, Object b){};
この二つだと型推論で上は(String, int)、下は(String, Integer)となって、プリミティブ型のintとラッパークラスのオブジェクト型のIntegerで型が厳密には違ってくるんじゃないかと。
例として挙げるには少し不適切かもしれません。

投稿2016/08/06 08:58

intelf___

総合スコア868

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2016/08/07 01:16

使い方が決まっているメソッドに関しては、型安全の考え方を採用し、使い方に幅がある、もしくはわからない場合は多態性の考えを取り入れるわけですね。 なるほど、合理的ですね。
guest

0

CS学んだわけではないので、
もっと詳しい方マサカリ投げてください。


意図を外した回答

> 多態性は曖昧に捉えることでメリットを得ている ArrayListはListですが、 ObjectはStringではありません。 Listを要求していて、内部でListとして扱うなら ArrayListを渡しても問題無いですね。 Objectを要求していて、内部でStringとして扱うなら String以外渡すとエラーなのにString以外を渡せるので問題ですね。 Listを要求していて、内部でArrayListとして扱うなら ArrayList以外のListを渡すとエラーなのにArrayList以外のListを渡せるので問題ですね。 Objectを要求していて、内部でObjectとして扱うなら Stringを渡しても問題無いですね。 ArrayListはListとしての振る舞いを持ちますね、 ArrayListをListとして扱えるのは多態の例です。 ObjectはStringとしての振る舞いを持ちませんね、 ObjectをStringとして扱えるのは多態の例ではないです。

型安全について不安になったのでちょっと調べました。

言葉の定義の問題なので、質問の意図とは外れますが、

型安全はこれとは逆に変数には可能な限り厳密な型を指定しようという考え方です。

型安全をこう捉えるなら、多態性とは確かに相反しますが、
そもそも「型安全」は
「型に対する不正な操作を実行時に検出して異常終了させる」も安全側に含みます。
つまり、実行時にすらエラーを吐かないものを「型が安全でない」というらしいです。

静的型付けか動的型付けかは型安全性と関係ないです。


長ったらしいので簡潔に纏めると
あなたの型安全(と多態)の認識がズレてませんか?

投稿2016/08/06 05:46

編集2016/08/07 02:57
ozwk

総合スコア13512

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

matobaa

2016/08/06 06:01 編集

あれれ。それを言うなら逆では? 「ArrayListはListです、StringはObjectです」「ListはArrayListではありません。ObjectはStringではありません」
ozwk

2016/08/06 06:04

逆ですか?
matobaa

2016/08/06 06:40

編集ありがとうございます。誤解を生む点は解消されています。
asahina_dev

2016/08/06 12:15

ArrayListはList*です* StringはObject*です* ListはArrayList*ではない場合があります。* ObjectはString*ではない場合があります。* かと
退会済みユーザー

退会済みユーザー

2016/08/07 23:56

回答ありがとうございます。 wikiを見ると、プログラムで定義していない状態や変数が出ない性質を安全性といい、型にまつわる安全性を型安全性というらしいですね。 ozwkさんの考える「型安全」という言葉はこれと比較的近いでしょうか。
ozwk

2016/08/08 00:02

そうですね。 「javaは型安全です」とどこかのサイトや本で読んで今回の疑問に至ったと思うのですが、 この「型安全」はwikipediaに書いてあるような意味か、「静的型付け」のことを指していると思います。
退会済みユーザー

退会済みユーザー

2016/08/08 00:14

そうすると、変数の型を絞り込むことにより、得られる安全性(ある変数に想定外のインスタンスが入ったりしなくなる)はまた別の話ということでしょうか。
ozwk

2016/08/08 00:20

そうですね、どちらかと言えば静的型付けの話ですね。
退会済みユーザー

退会済みユーザー

2016/08/08 00:29

わかりました。 うーん、他の言語を知らないので、あまりよくわからないのですが、変数を宣言するのが静的型付けで、変数を宣言しないのが動的型付けだと認識していたのですが、それは適当ではないということですよね? そうすると、「型を絞り込むことにより得られるメリット」と「型を曖昧にすることによりメリット」の両方が存在すると思うのですが、それらをどのように区別しているのか、というのが私の質問になります。 定義が間違っていたことに関しては私の勉強不足でした。 ご指摘ありがとうございました。
ozwk

2016/08/08 02:47

「型を曖昧」と言われても、ArrayListはListなんだから曖昧でもなんでもないと思うんですよ。 Listで充分だからListを要求しているところにListであるArrayListを渡すのと、 Stringが必要なところをObjectとして要求してStringではないInterger渡すのでは全然状況が違いますよね。
退会済みユーザー

退会済みユーザー

2016/08/08 09:58

おっしゃる通りです。 回答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問