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

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

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

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

Q&A

解決済

3回答

1413閲覧

ポリモフィズムで作成したクラス内の値を変更したい

stuokneo01

総合スコア7

Java

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

0グッド

1クリップ

投稿2022/01/01 06:57

編集2022/01/01 08:20

前提・実現したいこと

Javaで四則演算を行うためのプログラムを作っています。
ポリモフィズムの学習のために、インタフェースや抽象クラスを用いて、定数や変数等を改めてクラス定義して作っています。
この際、変数の値を変更した演算結果をそれぞれ出力するのですが、一度初期化した後に変数を変更しても演算結果に変化が表れません。

初期化後に変数の値を変更した場合は、その変数が含まれているクラスには影響しないのでしょうか。

Javaやポリモフィズム等についてまだ理解が浅いため、説明に間違いがあると思われますが、寛大に見ていただけると幸いです。

有識者の方、ご教示お願いします。

該当のソースコード

Java

1interface Expression{ 2 double getValue(); // 演算結果を返す 3 int getPriority(); // 演算の優先順位を返す 4} 5 6abstract class BinaryOperator implements Expression{ 7 private Expression left, right; 8 9 public BinaryOperator(Expression left, Expression right){ 10 this.left = left; 11 this.right = right; 12 } 13 14 public abstract String getSymbol(); // 演算記号を返す 15 16 // 計算式の文字列を返す 17 public String toString(){ 18 return "(" + left + getSymbol() + right + ")"; 19 } 20} 21 22// 定数に対応 23class Num implements Expression{ 24 private double value; 25 26 Num(double value){ 27 this.value = value; 28 } 29 30 public double getValue(){ 31 return this.value; 32 } 33 34 public int getPriority(){ 35 return 0; 36 } 37 38 public String toString(){ 39 return "" + this.value; 40 } 41} 42 43// 変数に対応 44class Var implements Expression{ 45 private String name; // 変数名 46 private double var; // 変数の値 47 48 Var(String name, double var){ 49 this.name = name; 50 this.var = var; 51 } 52 53 public double getValue(){ 54 return this.var; 55 } 56 57 public int getPriority(){ 58 return 0; 59 } 60 61 // 変数の値を引数の値に変更 62 public void setValue(double a){ 63 this.var = a; 64 } 65 66 public String toString(){ 67 return this.name; 68 } 69} 70 71// 足し算 72class Add extends BinaryOperator{ 73 private double a; 74 private double b; 75 76 Add(Expression a, Expression b){ 77 super(a, b); 78 this.a = a.getValue(); 79 this.b = b.getValue(); 80 } 81 82 public String getSymbol(){ 83 return "+"; 84 } 85 86 public double getValue(){ 87 return this.a + this.b; 88 } 89 90 public int getPriority(){ 91 return 0; 92 } 93} 94 95public class Rep3 { 96 public static void main(String[] args){ 97 // 変数の定義 98 Var x = new Var("x", -10.0); 99 100 // 計算式の定義 101 Expression formula = new Add(x, new Num(50.0)); 102 103 // 計算式の出力 104 System.out.println("式:" + formula.toString()); 105 System.out.println("x = " + x.getValue() + " のときの値は" + formula.getValue()); 106 107 // 変数xの値の-10から10までの整数値で試す 108 for(int i = 0; i < 20; i++){ 109 x.setValue(x.getValue() + 1); 110 System.out.println("x = " + x.getValue() + " のときの値は" + formula.getValue()); 111 } 112 } 113} 114
// 出力結果 式:(x+50.0) x = -10.0 のときの値は40.0 x = -9.0 のときの値は40.0 x = -8.0 のときの値は40.0 x = -7.0 のときの値は40.0 x = -6.0 のときの値は40.0 x = -5.0 のときの値は40.0 x = -4.0 のときの値は40.0 x = -3.0 のときの値は40.0 x = -2.0 のときの値は40.0 x = -1.0 のときの値は40.0 x = 0.0 のときの値は40.0 x = 1.0 のときの値は40.0 x = 2.0 のときの値は40.0 x = 3.0 のときの値は40.0 x = 4.0 のときの値は40.0 x = 5.0 のときの値は40.0 x = 6.0 のときの値は40.0 x = 7.0 のときの値は40.0 x = 8.0 のときの値は40.0 x = 9.0 のときの値は40.0 x = 10.0 のときの値は40.0

試したこと

全パターンで配列を用意してループ中に初期化を行うと、目的の結果が出力されますが、できればこの方法は使いたくないと思っています。

補足情報(FW/ツールのバージョンなど)

Java 11.0.12

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

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

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

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

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

jimbe

2022/01/01 07:35

まず、作りすぎです。 学習中であればなおさら、例えば足し算だけの x+1 で同様のテストが出来るはずです。 その上で、どのタイミングでロジック内のどの変数の値が幾つになっているはずなのか、実際は幾つになっているのか…を確認していく作業が必要です。 コードを考えて書くだけでなく、それが本当に考えたように出来ているのかを確認することも、プログラミングの一部です。
stuokneo01

2022/01/01 08:33

修正依頼ありがとうございます。 ご指摘の通り、少し簡単にしたコードに修正しましたので、もう一度見ていただけると有難いです。
guest

回答3

0

ベストアンサー

…………ああ、そういうことね。

つまり、mainメソッド内にあるfor文のところで刻々とxの値が変わるのに、なぜか(足し算等の)計算が固定になるってことですよね? それは当たり前です。

そもそも今回は殆どポリモーフィズムとか関係ありません。関係ないわけではありませんが、少なくともそれ以前の問題です。

まず、コードを読みましょう。コードを読むコツは「一行レベルで、その行が何をしているかを考えながら読む」です。

まずはmainメソッドからやってみましょう。

Java

1public class Rep3 { 2 public static void main(String[] args){ 3 // xというオブジェクトを生成 ( x = -10.0 ) 4 Var x = new Var("x", -10.0); 5 6 // formulaオブジェクトを生成( formula = x + 50.0 ) 7 Expression formula = new Add(x, new Num(50.0)); 8 9 // 計算して出力 10 System.out.println("式:" + formula.toString()); 11 System.out.println("x = " + x.getValue() + " のときの値は" + formula.getValue()); 12 13 // 変数xの値の-10から10までの整数値で試す 14 for(int i = 0; i < 20; i++){ 15 // xの値を変える 16 x.setValue(x.getValue() + 1); 17 // 結果を出力 18 System.out.println("x = " + x.getValue() + " のときの値は" + formula.getValue()); 19 } 20 } 21}

うーん、そんなに問題なさそうですね。

では、今までに呼び出されたコンストラクタやメソッド等も同様にやってみましょう。

すると、Addクラスとかでのコンストラクタでは

Java

1 Add(Expression a, Expression b){ 2 // 親のコンストラクタを呼び出す 3 super(a, b); 4 // 自身のa, b にセット。ただし、double型として。 5 this.a = a.getValue(); 6 this.b = b.getValue(); 7 }

となっていますが、これってどういう意図でしょうか?

実際にシミュレーションしてみてください。

mainメソッドのfor文でのxの値の変更の部分でxの値が書き換わっても、Addクラス側のa,bとは別物です。単に値をコピーしているだけなので。

どうしてもxオブジェクトを変えるとformulaオブジェクトにも反映させるのであれば、参照を使います。

簡単に言えば、double型としてa,bを保持するのではなく、Expression型として保持するのです。

Java

1// 足し算 2class Add extends BinaryOperator{ 3 private Expression a; // <- ここと 4 private Expression b; // <- ここと 5 6 Add(Expression a, Expression b){ 7 super(a, b); 8 this.a = a; // <- ここと 9 this.b = b; // <- ここ 10 } 11 12 public String getSymbol(){ 13 return "+"; 14 } 15 16 public double getValue(){ 17 return this.a + this.b; 18 } 19 20 public int getPriority(){ 21 return 0; 22 } 23}

と言う風に。

そうすれば、xオブジェクトの値が書き換わればAddクラスが持つa,bの状態も変更されます。(参照なので)

質問にあるやり方ですと、質問者さんが何か似顔絵を書いてそれをある人に渡す。その人はコピー機で複製しているとする。で、質問者さんが似顔絵に何か追加(たとえばキャラに道具を持たせるとか)して加工しました。ですが先ほど複製されたやつには反映されていません。「なんで反映されねーーーんだよ!!!?」という状態かと。

そりゃそうだろ…と言いたくなりますよね。

本来は、たとえば質問者さんが書いた似顔絵を黒板かどこかに張り付ける。そしてある人はそれを直に使う…みたいにすればいいはずです。


以降は蛇足なので、スルーしていいです。

参照についてもっと理解したいのであれば、C言語(C++でもいいけど)を理解すると割とすんなりいきます。
C言語でいうポインタのような感じですから。厳密にはポインタとも違いますが、イメージ的に。

投稿2022/01/01 09:08

編集2022/01/01 10:26
BeatStar

総合スコア4958

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

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

stuokneo01

2022/01/01 10:10

ありがとうございます。 フィールドの型を合わせて反映できるようにすることが必要であることがわかりました。 無事出力が求めるものになりました。 まだ参照渡しについては完全に理解できているとは言えませんが、雰囲気はなんとなくつかめたと思っています。 ありがとうございました。
guest

0

各演算子のフィールドa,bをdoubleではなくExpressionで持ち、getValue()の度に再計算するようにしましょう

投稿2022/01/01 07:27

ozwk

総合スコア13521

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

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

stuokneo01

2022/01/01 08:31

演算子のフィールドの型をExpressionで持たせ、getValue()メソッドでは以下のように変更しました。 public double getValue(){ return this.a.getValue() + this.b.getValue(); } しかし、NullPointerExceptionのエラーが出ました。もしよろしければ、もう少し具体的な修正方法をお教えいただけないでしょうか。
ozwk

2022/01/01 08:44

変更後のコードを追記してください
stuokneo01

2022/01/01 09:00

変更後のコードになります。 interface Expression{ double getValue(); // 演算結果を返す int getPriority(); // 演算の優先順位を返す } abstract class BinaryOperator implements Expression{ private Expression left, right; public BinaryOperator(Expression left, Expression right){ this.left = left; this.right = right; } public abstract String getSymbol(); // 演算記号を返す // 計算式の文字列を返す public String toString(){ return "(" + left + getSymbol() + right + ")"; } } // 定数に対応 class Num implements Expression{ private double value; Num(double value){ this.value = value; } public double getValue(){ return this.value; } public int getPriority(){ return 0; } public String toString(){ return "" + this.value; } } // 変数に対応 class Var implements Expression{ private String name; // 変数名 private double var; // 変数の値 Var(String name, double var){ this.name = name; this.var = var; } public double getValue(){ return this.var; } public int getPriority(){ return 0; } // 変数の値を引数の値に変更 public void setValue(double a){ this.var = a; } public String toString(){ return this.name; } } // 足し算 class Add extends BinaryOperator{ // フィールドの型を変更 private Expression a; private Expression b;   // 代入文を削除 Add(Expression a, Expression b){ super(a, b); } public String getSymbol(){ return "+"; } // getValue()を呼び出し public double getValue(){ return this.a.getValue() + this.b.getValue(); } public int getPriority(){ return 0; } } public class Rep3 { public static void main(String[] args){ // 変数の定義 Var x = new Var("x", -10.0); // 計算式の定義 Expression formula = new Add(x, new Num(50.0)); // 計算式の出力 System.out.println("式:" + formula.toString()); System.out.println("x = " + x.getValue() + " のときの値は" + formula.getValue()); // 変数xの値の-10から10までの整数値で試す for(int i = 0; i < 20; i++){ x.setValue(x.getValue() + 1); System.out.println("x = " + x.getValue() + " のときの値は" + formula.getValue()); } } }
ozwk

2022/01/01 09:08

親であるBinaryOperatorが既にExpressionを持ってます
guest

0

どう修正するかよりも何が起こっているか?を正しく理解した方が早い気がします。
というわけで、サンプルです。

初期化後に変数の値を変更した場合は、その変数が含まれているクラスには影響しないのでしょうか。

実際には、変数の値を変更できていないことに気づければよいのですが。

java

1public class Main { 2 public static void main(String[] args){ 3 // 変数の定義 4 Var x = new Var(-10.0); 5 6 Expression formula = new Num(x); 7 8 // 変数xの値の-10から10までの整数値で試す 9 for(int i = 0; i < 20; i++){ 10 x.setValue(x.getValue() + 1); 11 System.out.println("x = " + x.getValue() + " のときの値は" + formula.getValue()); 12 } 13 System.out.println("↑と"); 14 System.out.println("--------------------"); 15 System.out.println("↓の違いが、わかれば修正方法もわかるはず"); 16 17 // 変更できた場合 18 x.setValue(-10.0); // Var x = new Var(-10.0); の代わり 19 formula = new Num2(x); 20 // 以降は、上の式をコピペ 21 // 変数xの値の-10から10までの整数値で試す 22 for(int i = 0; i < 20; i++){ 23 x.setValue(x.getValue() + 1); 24 System.out.println("x = " + x.getValue() + " のときの値は" + formula.getValue()); 25 } 26 27 } 28} 29// 30interface Expression{ 31 double getValue(); // 演算結果を返す 32} 33// 34class Var { 35 private double var; // 変数の値 36 37 Var(double v) { 38 var = v; 39 } 40 public double getValue() { 41 return this.var; 42 } 43 44 // 変数の値を引数の値に変更 45 public void setValue(double a) { 46 this.var = a; 47 } 48} 49// Wrong case 50class Num implements Expression{ 51 private double value; 52 Num(Var value){ 53 this.value = value.getValue(); 54 } 55 public double getValue(){ 56 return this.value; 57 } 58} 59// 60class Num2 implements Expression{ 61 private Var value; 62 Num2(Var value){ 63 this.value = value; 64 } 65 public double getValue(){ 66 return this.value.getValue(); 67 } 68}

投稿2022/01/01 09:55

momon-ga

総合スコア4820

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問