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

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

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

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

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

Q&A

解決済

3回答

10835閲覧

Spring Bootでインターフェースを使ってポリモーフィズムを行う方法

退会済みユーザー

退会済みユーザー

総合スコア0

Java

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

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

0グッド

1クリップ

投稿2018/10/02 08:33

編集2018/10/02 08:54

以下のようなコードがあったとして、条件分岐で処理を切り分けるコードについて、
現状では、ObjectA、ObjectBともに@Autowiredをつけているので、コードを実行すると2つのオブジェクトが生成されてしまいます。
フィールド変数に複数のオブジェクト変数を用意するのはポリモーフィズムの観点でも気味が悪いので、インターフェースを使おうとしています。
しかし、nullpointエラーが発生するなどして前に進みません。
どのように対処したらよろしいのでしょうか。

@Autowired ObjectA objA; @Autowired ObjectB objB; @Override public void run(String command) { String msg; if (command == "A") { msg = objA.hoo(); return msg;   } if (command == "B") { msg = objB.hoo(); return msg;   } }

インターフェース版

@Autowired Objectable obj; @Override public void run(String command) { String msg; if (command == "A") { msg = objA.hoo(); return msg;   } if (command == "B") { msg = objB.hoo(); return msg;   } }

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

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

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

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

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

guest

回答3

0

すべてのコードを拝見していないため、ある程度推測で回答することになりますがご了承ください。

現状では、ObjectA、ObjectBともに@Autowiredをつけているので、コードを実行すると2つのオブジェクトが生成されてしまいます。

フィールド変数に複数のオブジェクト変数を用意するのはポリモーフィズムの観点でも気味が悪いので、インターフェースを使おうとしています。
しかし、nullpointエラーが発生するなどして前に進みません。
どのように対処したらよろしいのでしょうか。

上記のご説明と記載コードから以下の通り解釈しましたが相違ないでしょうか?
・ObjectA/ObjectBは同一インターフェースを実装している
・実装クラスが@Autowiredアノテーションを付与しているにも関わらずnullになる

解決したいこと
1.単一変数でObjectA、Bどちらも管理したい
2.上記を満たした上でnullを回避したい

解決したいことに対する回答
まず、nullになる件ですが、実装クラスがComponentScanの対象クラスであるかを確認してください。

ポリモーフィズムを意識して単一変数で取り回した処理例を以下に記載します。
ObjectA、Bを@Componentでコンポーネント化後にMap形式で保持するようにしました。

JAVA

1@Component("A") //Bean名を A でコンポーネント化 2public class ObjectA implements Objectable { 3 @Override 4 public String hoo() { 5 return "hooA"; 6 } 7 8} 9 10@Component("B") //Bean名を B でコンポーネント化 11public class ObjectB implements Objectable { 12 @Override 13 public String hoo() { 14 return "hooB"; 15 } 16}

java

1@Autowired // keyがBean名、valueが実装クラスの実インスタンス 2Map<String, Objectable> hoge; 3 4@Override 5public void run(String command) { 6 7 return hoge.get(command).hoo(); 8}

一応動くであろうコードを記載しました。
しかし、本来であればBean名は意味のある名称にするべきであるため
例えば以下のコードのように、セッター経由で変数に追加する際にそのクラス固有のあたいをキーに
設定してあげるのもアリだと思います。
(動作未確認)

java

1public interface Objectable { 2 public String getCommandName(); // command = A, Bのあたい格納用 3 4 public String hoo(); 5 6}

java

1private Map<String, Objectable> hoge = new HashMap<String, Objectable>(); 2 3@Override 4public void run(String command) { 5 6 return hoge.get(command).hoo();; 7} 8 9@Autowired 10public void setObjectableList(List<Objectable> l) { 11 12 for( Objectable o : l ) { 13 this.hoge.put(o.getCommandName(), o); 14 } 15}

投稿2018/10/04 12:43

編集2018/10/04 12:54
Lazy-player

総合スコア77

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

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

退会済みユーザー

退会済みユーザー

2018/10/11 07:44

ご回答ありがとうございます。 @Autowired Map<String, Objectable> hoge; @Override public void run(String command) { return hoge.get(command).hoo(); } こちらの方法で実装した場合、ObjectA・ObjectB双方のオブジェクトが生成されてしまうようです。 一つのオブジェクトだけ生成される方法があればご教授頂きたいです。
Lazy-player

2018/10/11 10:24

既にA-pZさんが回答されておりますのでそちらを参照して頂ければ対応可能かと思います。
guest

0

ベストアンサー

やりたいことは例えばこういうことなのだろうか…という推察からですが、

java

1@Service 2@RequiredArgsConstructor 3public class DynamicBeanService { 4 5 private final ItemRepository itemRepository 6 7 public String getResult(String type) { 8 return itemRepository.select(); 9 } 10}

としたときに、このItemRepositoryがインタフェースで、これの実装クラスが、

  • FileItemRepository
  • DatasourceItemRepository

と複数あった場合、@AutowiredやコンストラクタインジェクションしようとしてもSpringBoot起動時のBeanチェックで落ちてしまう(=DIコンテナから見たら、ItemRepositoryの実体を取得しようにも、どちらを取得してよいか判断できない)ので、条件によって取得するBeanを振り分けるようにする例として、SpringBootの設定を使う手法がもっとも簡単でしょうか。


インタフェース

java

1public interface ItemRepository { 2 public String select(); 3}

実装クラス

java

1import org.springframework.stereotype.Repository; 2 3@Repository 4public class FileItemRepository implements ItemRepository { 5 @Override 6 public String select() { 7 return "file"; 8 } 9}

java

1import org.springframework.stereotype.Repository; 2 3@Repository 4public class DatasourceItemRepository implements ItemRepository { 5 @Override 6 public String select() { 7 return "datasource"; 8 } 9}

このような実装をしたときに、SpringBootのBean定義をします。

java

1import org.springframework.beans.factory.annotation.Autowired; 2import org.springframework.beans.factory.config.ConfigurableBeanFactory; 3import org.springframework.context.annotation.Bean; 4import org.springframework.context.annotation.Configuration; 5import org.springframework.context.annotation.Scope; 6 7import com.github.apz.springsample.repository.DatasourceItemRepository; 8import com.github.apz.springsample.repository.FileItemRepository; 9import com.github.apz.springsample.repository.ItemRepository; 10 11@Configuration 12public class DynamicBeanSampleConfig { 13 14 @Autowired 15 FileItemRepository fileItemRepository; 16 17 @Autowired 18 DatasourceItemRepository datasourceItemRepository; 19 20 @Bean 21 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) 22 public ItemRepository itemRepository(String type) { 23 switch (type) { 24 case "FILE": 25 return fileItemRepository; 26 case "DATA_SOURCE": 27 return datasourceItemRepository; 28 default: 29 throw new UnsupportedOperationException(); 30 31 } 32 } 33}

この例では、itemRepositoryの名前でDIコンテナに登録します。引数にStringを要求し、その値でどのRepositoryの実態を返すかを判断します。
なお、Scopeは、itemRepositoryのインスタンスが要求されるたびに動的に変わるのでProtoTypeを指定します。

こうすると、このRepositoryを取得する側の実装は、例えば次のようになるでしょう。

java

1import org.springframework.context.ApplicationContext; 2import org.springframework.stereotype.Service; 3 4import com.github.apz.springsample.repository.ItemRepository; 5 6import lombok.RequiredArgsConstructor; 7 8@Service 9@RequiredArgsConstructor 10public class DynamicBeanService { 11 12 private final ApplicationContext context; 13 14 public String getResult(String type) { 15 ItemRepository itemRepository = 16 (ItemRepository)context.getBean("itemRepository", type); 17 18 return itemRepository.select(); 19 } 20}

投稿2018/10/04 14:19

A-pZ

総合スコア12011

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

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

退会済みユーザー

退会済みユーザー

2018/10/11 07:55

ご回答ありがとうございます。 spring boot初心者なので私の理解が正しくないかもしれませんが、 @Autowired FileItemRepository fileItemRepository; @Autowired DatasourceItemRepository datasourceItemRepository; これら2つを定義したとき、spring bootを起動すると2つのオブジェクトが生成されるかと思います。 できれば、生成されるオブジェクトは、String typeで指定したものに紐づくオブジェクトに限定したいです。 例えば、typeがFILEだった場合、spring bootを起動してから、実行結果が出るまでに生成されるオブジェクトは、FileItemRepositoryのみにするといった具合です。 そのような方法はあるのでしょうか。
A-pZ

2018/10/11 08:27

起動時に指定をしたい場合は、対象のクラスに `@Primary` をつけるだけで十分です。
guest

0

とりあえず、

java

1if(command == "A")

などは

java

1if(command.equals("A"))

にしないと。

投稿2018/10/04 10:17

swordone

総合スコア20651

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

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

A-pZ

2018/10/05 01:39

回答になっていないため。
退会済みユーザー

退会済みユーザー

2018/10/11 07:39

ご回答ありがとうございます。 そうですね。teratailのエディタ上でそのまま書いていたので、他の言語で慣れている書き方をしてしまいました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問