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

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

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

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

Q&A

3回答

6805閲覧

【Java】総称型のリストと継承について

takenyaan

総合スコア119

Java

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

0グッド

0クリップ

投稿2017/04/28 15:11

編集2017/04/28 15:15

はじめまして。
タイトルの一言でうまく説明できないのですが、下記のようなクラス構成を考えています。

  • AクラスはBクラスの親
  • AElementクラスはBElementクラスの親
  • AクラスはAElementクラスのリストをフィールドとして保持する
  • BクラスはBElementクラスのリストをフィールドとして保持する
class A { private final List<? extends AElement> elements; public A(List<? extends AElement> elements) { this.elements = elements; } public List<? extends AElement> getElements() { return this.elements; } } class B extends A { public B(List<? extends BElement> elements) { super(elements); } } class AElement { } class BElement extends AElement { }

この時Bクラスのインスタンスにて、getElements()を呼出した際に、BElementsのリストを返却したいです。
下記のようにBクラスにてgetElements()をオーバーライドすれば取得できますが、型安全でないキャストを行っており、
そもそもの設計が破たんしているのではないかと考えています。

class B extends A { public B(List<? extends BElement> elements) { super(elements); } @Override @SuppressWarnings("unchecked") public List<? extends BElement> getElements() { return (List<? extends BElement>) super.getElements(); } }

このようなケースにてどのように書くのが or 設計するのが適切でしょうか。
なお、クラスに総称型を付与する対応が一番に考えられる(下記参照)と思いますが、A、Bクラスの呼出元に総称型を意識させたくありません。※AクラスとAElementクラス、BクラスとBElementクラスの紐付きは自明のものとするため。

class A<T extends AElement> { private final List<T> elements; public A(List<T> elements) { this.elements = elements; } public List<T> getElements() { return this.elements; } } class B<T extends BElement> extends A<T> { public B(List<T> elements) { super(elements); } } class AElement { } class BElement extends AElement { }

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

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

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

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

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

guest

回答3

0

直接的な回答ではないですが、

ジェネリクスには共変・非変・反変という概念があります。このキーワードで調べるとよい記事が見つかります。

Javaのジェネリクスは基本的に非変として設計されておりextends, superを一部の箇所で用いることで共変・反変的な扱いができるようになっていますが、より自然に共変・非変・反変を扱える言語もあります。

例えばこういう記事があります。

質問者さんにとって「共変・非変・反変」という明確な用語を把握することと、設計における一般的な考え方(ある型を入力として扱ったり、出力として扱う場合の共変・非変・反変の考え方)を認識しておくと、どう設計するべきかを他者と議論する際に話が通じやすくなるためとても役立つと思います。

そもそもの設計が破たんしているのではないかと考えています。

キャストは一般的に悪と考えられていますが、ジェネリクスのクラスの設計においてJavaの言語仕様の不足からやむを得ないキャストは「あり得る」と自分は考えます。つまり設計が破たんするかどうかはそのクラスのインターフェースに矛盾があるかどうかで決まり、キャストしているかどうかで決まるわけではないというのが自分の意見です。

例えば単純な一つの例として以下のような設計もありと思いますが、それが質問者さんがイメージしていることかどうかは、現状の質問だけからははっきりしない気がします。

java

1class AE { 2} 3 4class BE extends AE { 5} 6 7class A<T extends AE> { 8 List<T> list; 9 10 List<T> elements() { return list; } 11} 12 13class B extends A<BE> { 14}

投稿2017/04/28 21:58

編集2017/04/28 22:05
KSwordOfHaste

総合スコア18394

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

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

0

AとBが継承関係にあることが原因で無理が生じているようにも思えます。
総称型を意識させたくないというのであれば、私ならこうしますが

java

1//「リストを取得する」抽象メソッドを持ったインタフェースか抽象クラスを定義し、ここに総称型を持たせる 2interface ElementsManager<E> { 3 List<? extends E> getElements(); 4} 5 6//実装するときに総称型付きで実装する 7class A implements ElementManager<AElement> { 8 private final List<? extends AElement> elements; 9 10 public A(List<? extends AElement> elements) { 11 this.elements = elements; 12 } 13 14 public List<? extends AElement> getElements() { 15 return this.elements; 16 } 17} 18 19class B implements ElementManager<BElement> { 20 private final List<? extends BElement> elements; 21 22 public A(List<? extends BElement> elements) { 23 this.elements = elements; 24 } 25 26 public List<? extends BElement> getElements() { 27 return this.elements; 28 } 29} 30 31//AもBも受け取りたいような場合、メソッドの引数で型制限をかける 32public List<E extends AElement> getElements(ElementManager<E> manager) { 33 return manager.getElements(); 34}

投稿2017/04/30 01:21

swordone

総合スコア20651

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

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

0

どのような設計がどのデザインパターンに合致していてベストプラクティスであるかなどという議論は全くできませんので私見を述べているとお考えください。

具体的にどのような経緯でそのような設計に至ったのかがよく理解できていないのですが、個人的には継承元と継承先のエンティティクラスがあり、それらは実際に別々に使用され、またそれらを扱うためのクラスも継承関係のもとで別々に存在し、かつそれぞれがそれぞれのエンティティのリストをプロパティに持つというのは、いまいち利用シーンが思いつきません。
やりたいことは、以下のようなことと受け取りました。

1.エンティティAElementがある
2.AElementを継承するエンティティBElementがある
3.AElementをリストで管理したい(このリストにはAElementを継承するBElementも一部含まれる←この解釈は合っていますか?)
4.BElementのみをリストで管理したい

この要件だと、特に3と4の管理クラスには継承関係を持たせる必要性を感じませんし、そもそも3と4を実現するためのクラスを明確に分ける必要性も感じません。
例えばですが、1つの管理クラスの中に3.のリストと4.のリストを持たせても良いと思いますし、1つの管理クラスで3.のリストのみを管理し、4.のリストが欲しいときは3.のリストから4.のみが含まれるリストを作って返しても良いと思いますし、管理クラスを2つ作ったとしても、それらの間に継承関係がある必要があるかというと私には無くてもよいのではないかと感じられます。

なお3.の注釈にも記載しましたが、解釈が合っているか自信がありません。どうもAElementのみのリストを管理したいのかな?という意図を思わせる書き方だったからです。
その場合、なぜわざわざAElementを継承してBElementを作ったのかが疑問です。
継承関係を無くすか、スーパークラスSuperElementを作り、AElementとBElementはそこから継承して別々に作ったほうが自然のように思われます。

もう少し、その設計に至った経緯等の情報が欲しいかなと思います。

投稿2017/04/29 16:00

akabee

総合スコア1947

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

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

KSwordOfHaste

2017/04/29 23:26

自分の回答で「現状の質問だけからははっきりしない気がします」といったことをakabeeさんが分かりやすく述べておられると思います。上記の議論の中にもA,Bが共変・反変のいずれにしたいかという話が含まれています>質問者さん。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問