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

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

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

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

Q&A

解決済

3回答

13917閲覧

コンストラクタが例外を投げることについて

chankane

総合スコア139

Java

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

0グッド

0クリップ

投稿2017/05/13 23:25

###前提・実現したいこと
こんにちは。毎度お世話になっております。
現在 Java でボンバーマンを作っていますが、発生しているエラーの処理の仕方がわかりません。
聞きたいことは3つです。

  1. 例外をどうやって補足するのか
  2. コンストラクタが例外を throw することはそもそも好ましくないのか
  3. その他修正すべきところ。

よろしくお願いいたします。

###発生している問題・エラーメッセージ

Player.java:8: エラー: クラス GameObjectのコンストラクタ GameObjectは指定された 型に適用できません。 Player(int x, int y){ ^ 期待値: String,int,int 検出値: 引数がありません 理由: 実引数リストと仮引数リストの長さが異なります Player.java:11: エラー: superの呼出しはコンストラクタの先頭文である必要がありま す super("player.png", x, y); ^ Player.java:41: エラー: クラス GameObjectのコンストラクタ GameObjectは指定された 型に適用できません。 Bomb(){ ^ 期待値: String,int,int 検出値: 引数がありません 理由: 実引数リストと仮引数リストの長さが異なります Player.java:44: エラー: superの呼出しはコンストラクタの先頭文である必要がありま す super("bomb.png", -GameObject.SIDE, -GameObject.SIDE); ^ エラー4個

###該当のソースコード1

java

1// Player.java 2public class Player extends GameObject{ 3 private static final int MAX_CAPACITY = 8; 4 5 private Bomb[] bomb; 6 private int capacity; 7 8 Player(int x, int y){ 9 //super("player.png", x, y); 10 try{ 11 super("player.png", x, y); 12 }catch(Exception e){ 13 e.printStackTrace(); 14 } 15 this.capacity = 1; 16 try{ 17 this.bomb = new Bomb[MAX_CAPACITY]; 18 for(Bomb b : bomb){ 19 // 最初は画面外に配置 20 b = new Bomb(); 21 } 22 }catch(Exception e){ 23 e.printStackTrace(); 24 } 25 } 26 27 void putBomb(){ 28 if(capacity == 0){ 29 return; 30 } 31 this.bomb[capacity-1].put(this.x, this.y); 32 this.capacity--; 33 } 34} 35 36class Bomb extends GameObject{ 37 private static final int TIME_LIMIT = 1000; 38 39 private int time_left; 40 41 Bomb(){ 42 //super("bomb.png", -GameObject.SIDE, -GameObject.SIDE); 43 try{ 44 super("bomb.png", -GameObject.SIDE, -GameObject.SIDE); 45 }catch(Exception e){ 46 e.printStackTrace(); 47 } 48 this.time_left = 0; 49 } 50 51 void put(int x, int y){ 52 this.x = x; 53 this.y = y; 54 this.time_left = this.TIME_LIMIT; 55 } 56 57 void countDown(){ 58 this.time_left--; 59 } 60}

###該当のソースコード2

java

1// GameObject.java 2import javax.swing.JPanel; 3import java.awt.Graphics; 4import java.awt.Image; 5import java.awt.image.BufferedImage; 6import java.io.File; 7import javax.imageio.ImageIO; 8import java.io.IOException; 9 10class GameObject{ 11 static final int SIDE = 40; 12 13 int dx, dy; 14 protected int x, y; 15 private Image image; 16 17 GameObject(String file_name, int x, int y) throws IOException{ 18 BufferedImage bi = ImageIO.read(new File(file_name)); 19 20 if(bi.getWidth() != this.SIDE || bi.getHeight() != this.SIDE) 21 throw new IOException("画像サイズは" + this.SIDE + "*" + this.SIDE + "でなければなりません. : " + file_name); 22 this.image = bi; 23 this.x = x; 24 this.y = y; 25 this.dx = 0; 26 this.dy = 0; 27 } 28 29 int getX(){ 30 return this.x; 31 } 32 33 int getY(){ 34 return this.y; 35 } 36 37 void setLocation(int x, int y){ 38 this.x = x; 39 this.y = y; 40 } 41 42 void move(){ 43 this.x += this.dx; 44 this.y += this.dy; 45 } 46 47 void draw(Graphics g, JPanel io){ 48 int x0 = this.x - this.SIDE/2; 49 int y0 = this.y - this.SIDE/2; 50 51 g.drawImage(this.image, x0, y0, io); 52 } 53}

###試したこと
ソースコード1に対してコメントアウトしている位置に super クラスのコンストラクタの記述を移動させたが、今度は例外が補足できない。

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

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

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

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

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

guest

回答3

0

ベストアンサー

やりたいことは、親クラス GameObject のコンストラクタがチェック例外を投げると宣言されている

GameObject::GameObject() throws IOException

この時、その子クラス Player のコンストラクタの中でチェック例外を補足して、例外を投げないコンストラクタとしたい

Player::Player()

ということですね。

結論から言うと、そのようなことはできません。

通常のメソッドをオーバーライドする場合は、親クラスのメソッド呼び出しで発生する例外を子クラスの実装側で補足することができますが、コンストラクタではできません。Javaの言語設計上の制約ですが、理由はちょっとわかりません。

よくある回避策は、Playerに static なファクトリメソッドを追加して、その中で例外を処理します。

public static Player makePlayer() { try { return new Player(...); } catch (IOException e) { e.printStackTrace(); } }

投稿2017/05/14 00:23

koko_u

総合スコア936

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

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

chankane

2017/05/14 00:33

ご回答ありがとうございました。そのような回避策があったのですね。初知りです Σ(・ω・ノ)ノ! 早速使用させていただきます。 勝手ながらフォローさせていただきます。
guest

0

「コンストラクタで例外を発生させる」というのは手法としてはありです。そして、「superした親クラスのコンストラクタで例外が発生する」ということは、親クラスとして正しく初期化されていないということなので、子クラスで続けられることはありません。newを呼んだ側へ例外を素通しする、以外の選択肢はありません。

投稿2017/05/13 23:38

maisumakun

総合スコア145183

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

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

chankane

2017/05/14 00:39

ご回答ありがとうございました。コンストラクタが例外を投げるのはありということですか。よくわかりました。なにか特別な事情がない限りは例外を素通ししたいと思います。今回は例外を上へ投げたくないということを記述するのを忘れていました。すみません。
guest

0

他の回答者さんの回答のとおりと思うのですが「なぜ」について・・・

Javaにせよその他のクラスベースのオブジェクト指向言語にせよ次のような想定があると思います。

  • 派生クラスのインスタンスは必ず基底クラスのインスタンスでなければならない

これは、派生クラスのインスタンスに対して「必ず」基底クラスのインスタンスに対する操作(メソッド)が行えることを保証する目的と言えると思います。

  • 基底クラスのいずれかのコンストラクターが正常に呼び出せれば「よし」という仕様が達成できるか?

もし基底クラスのあるコンストラクター呼び出しで例外が発生してもそれを捕捉できるようにするなら、基底クラスの別のコンストラクターを必ず呼び出して「正常に基底クラスの初期化」を行えるようにする必要があります。例えば

java

1class Derived extends Base { 2 Derived(int param1, int param2) { 3 try { 4 super(); 5 } catch (Exception e) { 6 try { 7 super(param1); 8 } catch (Exception e2) { 9 super(param1, param2); 10 } 11 } 12 ... Derivedの初期化処理 13 } 14}

このように書けたとして、コンパイラーは「基底クラスのコンストラクター呼び出しが成功するまでは、派生クラスのいかなる操作も許さない」ようにしなければならいないことでしょう。なぜなら「派生クラスの任意の操作は基底クラスとしての初期化が大前提であるから」です。そうしなければ最初の前提が崩れてしまいます。このような言語仕様にすることも可能だとは思います。しかしそれは言語仕様を大変複雑なものにするだけであってそれほどの価値はありません。
なぜなら「派生クラスが、基底クラスのどのコンストラクターで初期化すべきものか」は派生クラスを設計する際に分かっていると仮定することは充分合理的だからです。「どう初期化すればいいか実行時にやってみないとわからない」というような状況は考えにくいわけです。

こうした理由で言語設計者は「コンストラクター内で基底クラスのコンストラクター呼び出しの例外を捕捉する必要性はない」と判断することが一般的なのだと思います。

投稿2017/05/14 00:44

KSwordOfHaste

総合スコア18394

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

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

chankane

2017/05/15 10:30

こんにちは、毎度お世話になっております。 ごめんなさい。じっくり読んでいたので返信が遅くなりました。 そうですよね、初期化が大前提ですよね、そう考えると、基底クラスのコンストラクタを最初に記述しなければならない理由もよくわかります。 また、その大前提が崩れると言語として複雑になる。 だから、派生クラス内で例外を補足する必要はなく、むしろ補足するほうがその後の処理に関してデメリットが大きい。 ということですね。おおざっぱではありますが、理解できたと思います。 なんでコンストラクタだけ無理なんや!!と思っていたモヤモヤが解決いたしました。ありがとうございました(^^) またお世話になるとおもいます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問