HOME»基本情報技術者平成28年秋期問題»午後問11
基本情報技術者過去問題 平成28年秋期 午後問11
⇄問題文と設問を画面2分割で開く⇱問題PDF問11 ソフトウェア開発(Java)
次のJavaプログラムの説明及びプログラムを読んで,設問1,2に答えよ。
(Javaプログラムで使用するAPIの説明は,こちらを参照してください。)
〔プログラムの説明〕
整数値の加減乗除の演算をする電卓のプログラムである。この電卓は,数字キー,加減乗除の各演算キー,イコールキー及びクリアキーをもつ。プログラムは,キーが押されたとき,それぞれのキーに対応する処理を実行する。数値などの表示は,System.out.println を呼び出して行う。
(Javaプログラムで使用するAPIの説明は,こちらを参照してください。)
〔プログラムの説明〕
整数値の加減乗除の演算をする電卓のプログラムである。この電卓は,数字キー,加減乗除の各演算キー,イコールキー及びクリアキーをもつ。プログラムは,キーが押されたとき,それぞれのキーに対応する処理を実行する。数値などの表示は,System.out.println を呼び出して行う。
- インタフェース Key は,電卓のキーが押されたときの処理を実行するメソッドを定義する。
メソッド operateOn は,引数で与えられたクラス java.util.Stack のインスタンス(以下,スタックという)に対して,キーに対応する処理を実行する。 - 列挙 DigitKey は,数字キーを表す定数 DIGIT0~DIGIT9を定義する。
メソッド operateOn は,キーを10進数の入力として処理する。引数で与えられたスタックの先頭に格納されている値は0(初期値)又は入力中の数値であり,その値を更新する。 - 列挙 OperationKey は,加減乗除の各演算キー,イコールキー及びクリアキーを表す定数 ADD,SUBTRACT,MULTIPLY,DIVIDE,EQUAL 及び CLEAR を定義する。
メソッド operateOn は,加減乗除の各演算キーに対応する演算を,スタックの内容に対して実行する。 - クラス Calculator は,電卓本体を表す。フィールド stack は,電卓内部の数値の状態を表すスタックを保持する。フィールド pendingKey は,演算に必要な数値の入力が終わるまで演算キーを保持する。また,イコールキーが押されたときは,イコールキーを保持する。例えば,キーの定数 DIGIT2,ADD,DIGIT4 が順に処理されたとき,スタックに格納されている値は先頭から4,2であり,pendingKey の値は ADD である。次にキーの定数 EQUAL が処理されたとき,演算キー ADD の加算処理が実行され,スタックに格納されている値は6となり,pendingKey の値は EQUAL となる。ここで,二つの数値に対する加減乗除の演算結果は,Javaのint型の演算結果に一致するものとする。
メソッド onKeyPressed は,電卓のキーが押されたときに呼び出される。押されたキーは,引数で与えられる。押されたキ一及び電卓の内部状態に基づいて,処理を実行する。 - クラス CalculatorTest は,クラス Calculator をテストするプログラムである。
メソッド main は,まず,文字と電卓の各キーとの対応を作成し,クラス Calculator のインスタンスを生成する。次に,引数で与えられた文字列の各文字をキーの定数に変換し,そのキーの定数を引数としてクラス Calculator のインスタンスのメソッド onKeyPressed を呼び出す。例えば,メソッド main の引数として文字列"2*3=" が与えられたとき,それぞれの文字を,キーの定数 DIGIT2,MULTIPLY,DIGIT3,EQUAL に変換し,逐次それぞれのキーの定数を引数としてメソッド onKeyPressed を呼び出す。メソッド main を実行したときの出力を図1に示す。
設問1
プログラム中の に入れる正しい答えを,解答群の中から選べ。
a に関する解答群
- extends
- implements
- imports
- inherits
- requires
- throws
b,c に関する解答群
- ordinal()
- stack.peek()
- stack.pop()
- stack.push(0)
- stack.push(ordinal())
- values()
d に関する解答群
- DigitKey
- Key
- stack.pop()
- this
- val1
- val2
e に関する解答群
- Calculator
- Character
- DigitKey
- Integer
- Key
- OperationKey
解答選択欄
- a:
- b:
- c:
- d:
- e:
解答
- a=イ
- b=ウ
- c=ア
- d=エ
- e=オ
解説
〔aについて〕
列挙型(enum型)のクラス DigitKey および OperationKey の宣言部で、Key の前に入る用語を答える問題です。
〔プログラム1〕より、Key は インタフェースだとわかります。インタフェースは、全てのメソッドが抽象メソッドでメンバー変数を1つも持たず、別のクラスに実装されることを前提としたクラスです。あるインタフェースを子クラスで実装するときは implements キーワードとともに実装元のインタフェース名を指定します。「implements Key」で Key インタフェースを実装するという意味になります。
なお、インタフェースではなくクラスを継承するときには extends を用います。
∴a=イ:implements
〔b,cについて〕
クラス DigitKey のメソッド operateOn の中で行われる計算処理に関する問題です。
まず、〔プログラムの説明〕(2)に「メソッド operateOn は,キーを10進数の入力として処理する。引数で与えられたスタックの先頭に格納されている値は0(初期値)又は入力中の数値であり,その値を更新する」とあるように電卓の数字キーが押されたときの処理であること、および計算処理が[b]を10倍した値に[c]を加えていることを確認します。
次に、例として電卓の数字キーを押したときのことをイメージしてみます。「1」「2」と順番にキーを押したとき、電卓は「12」を表示します。2つの入力を「12」として扱うためには、「1×10+2」というように電卓の現在の数字を10倍してそれに押された数字キーの値を加算すれば適切な値となります。つまり、「1」を[b]に、「2」を[c]に置き換えれば答えが導き出せます。
[b]は先に入力されていた値なので、前述の〔プログラムの説明〕にあった「スタックの先頭に格納されている値」を使うと考えられます。スタックから値を取り出す操作には pop() と peek() があり、pop() がスタックの先頭要素を削除してからその値を返すのに対し、peek() は削除せずに返します。〔プログラムの説明〕には「その値を更新する」とあるので、pop() で先頭オブジェクトを削除してから、push() で演算結果をスタックに積む操作が適切となります(スタックの先頭の値を入れ替える)。
∴b=ウ:stack.pop()
[c]には押されたキーに対応する数値を得るための式が入ります。選択肢の中でこれに当てはまるのは ordinal() です。このメソッドは列挙型で使用でき、定数の序数(定義された順番)を返します。例えば、クラス Calculator の onKeyPressed の引数に DIGIT1 が与えられたとき、クラス Calculator は、DIGIT1 について operateOn() を呼び出します。このとき ordinal() は、列挙型 DigitKey 内での DIGIT1 の順番、すなわち1を返すことになります。なお、values() は、列挙型で定義された列挙子のリストを返すので誤りです。
∴c=ア:ordinal()
〔dについて〕
メソッド calculate 内の、switch 文の条件に関する問題です。
[d]直後の case 文に具体的な値が ADD、SUBSTRACT、MULTIPLY、DIVIDEと書かれています。これらは[d]を含むクラス OperationKey で定義されていること、また、同クラス内のもう一つのメソッド operateOn に記載された
∴d=エ:this
クラス CalculatorTest 内のコレクション型変数 map の定義に関する問題です。コレクション型変数の<…>内部には、格納されるオブジェクトのクラス名を指定します。〔プログラムの説明〕(5)に「メソッド main は,まず,文字と電卓の各キーとの対応を作成し…」と記載されているので、map には以下のように文字と電卓の各キーとの対応が格納されると考えられます。〔プログラム 5〕で[e]の先を読み進めると、
∴e=オ:Key
列挙型(enum型)のクラス DigitKey および OperationKey の宣言部で、Key の前に入る用語を答える問題です。
〔プログラム1〕より、Key は インタフェースだとわかります。インタフェースは、全てのメソッドが抽象メソッドでメンバー変数を1つも持たず、別のクラスに実装されることを前提としたクラスです。あるインタフェースを子クラスで実装するときは implements キーワードとともに実装元のインタフェース名を指定します。「implements Key」で Key インタフェースを実装するという意味になります。
なお、インタフェースではなくクラスを継承するときには extends を用います。
∴a=イ:implements
〔b,cについて〕
クラス DigitKey のメソッド operateOn の中で行われる計算処理に関する問題です。
まず、〔プログラムの説明〕(2)に「メソッド operateOn は,キーを10進数の入力として処理する。引数で与えられたスタックの先頭に格納されている値は0(初期値)又は入力中の数値であり,その値を更新する」とあるように電卓の数字キーが押されたときの処理であること、および計算処理が[b]を10倍した値に[c]を加えていることを確認します。
次に、例として電卓の数字キーを押したときのことをイメージしてみます。「1」「2」と順番にキーを押したとき、電卓は「12」を表示します。2つの入力を「12」として扱うためには、「1×10+2」というように電卓の現在の数字を10倍してそれに押された数字キーの値を加算すれば適切な値となります。つまり、「1」を[b]に、「2」を[c]に置き換えれば答えが導き出せます。
[b]は先に入力されていた値なので、前述の〔プログラムの説明〕にあった「スタックの先頭に格納されている値」を使うと考えられます。スタックから値を取り出す操作には pop() と peek() があり、pop() がスタックの先頭要素を削除してからその値を返すのに対し、peek() は削除せずに返します。〔プログラムの説明〕には「その値を更新する」とあるので、pop() で先頭オブジェクトを削除してから、push() で演算結果をスタックに積む操作が適切となります(スタックの先頭の値を入れ替える)。
∴b=ウ:stack.pop()
[c]には押されたキーに対応する数値を得るための式が入ります。選択肢の中でこれに当てはまるのは ordinal() です。このメソッドは列挙型で使用でき、定数の序数(定義された順番)を返します。例えば、クラス Calculator の onKeyPressed の引数に DIGIT1 が与えられたとき、クラス Calculator は、DIGIT1 について operateOn() を呼び出します。このとき ordinal() は、列挙型 DigitKey 内での DIGIT1 の順番、すなわち1を返すことになります。なお、values() は、列挙型で定義された列挙子のリストを返すので誤りです。
∴c=ア:ordinal()
〔dについて〕
メソッド calculate 内の、switch 文の条件に関する問題です。
[d]直後の case 文に具体的な値が ADD、SUBSTRACT、MULTIPLY、DIVIDEと書かれています。これらは[d]を含むクラス OperationKey で定義されていること、また、同クラス内のもう一つのメソッド operateOn に記載された
this == EQUAL || this == CLEAR
をヒントに、this が適切だと判断できます。列挙型で自身の列挙子を参照するには this を使用します。∴d=エ:this
- 演算キーによって処理を分岐している部分なので、数字を表す DigitKey は不適切です。
- Key 実装しているインタフェースの名称であり、変数として定義されていないので使えません。また、クラスとしての Key は DigitKey と OperationKey の両方を包含していますので、意味が広すぎることからも不適切です。
- スタックに格納されているのは数字ですので、この分岐の条件式としては不適切です。
- 正しい。
- calculate の引数として渡される val1、val2 は oprerateOn にてスタックから取り出した数字ですので不適切です。
- 「オ」と同じ理由で不適当です。
クラス CalculatorTest 内のコレクション型変数 map の定義に関する問題です。コレクション型変数の<…>内部には、格納されるオブジェクトのクラス名を指定します。〔プログラムの説明〕(5)に「メソッド main は,まず,文字と電卓の各キーとの対応を作成し…」と記載されているので、map には以下のように文字と電卓の各キーとの対応が格納されると考えられます。〔プログラム 5〕で[e]の先を読み進めると、
//文字と列挙OperationKeyの定数の対応をmapに格納する。
map.put("…省略…", key);
//数字と列挙DigitKeyの定数の対応をmapに格納する。
map.put("…省略…", key);
という処理があり、map のvalue側には列挙型の OperationKey または DigitKey が格納されることがわかります。格納されるオブジェクトとして、OperationKey(=文字) とDigitKey(=数字)の両方があり得るので、map の変数の型には2つの列挙型の継承元である Key 型を指定し、両方の型を格納できるようにしておく必要があります(両方の型を Key 型としてざっくり扱う)。よって、[e]には「Key」が入ります。map.put("…省略…", key);
//数字と列挙DigitKeyの定数の対応をmapに格納する。
map.put("…省略…", key);
∴e=オ:Key
設問2
表1は,文字列を引数としてメソッド main を実行したときの出力の最後の行(図1の場合は6)を表している。表中の に入れる正しい答えを,解答群の中から選べ。ここで,プログラム中の には,全て正しい答えが入っているものとする。
f,g に関する解答群
- 0
- 2
- 4
- 8
- 16
- 32
- 64
- ArithmeticException
- Error
解答選択欄
- f:
- g:
解答
- f=イ
- g=ケ
解説
〔fについて〕
〔プログラム 5〕の main メソッドの以下を見ると
また「C」が処理されるときには、〔プログラム 4〕のメソッド onKeyPressed で、
まず「2」のキーが押されると、「2」に対応する DIGIT2 を引数としてメソッド onKeyPressed が呼び出され、以下の処理が実行されます。
そして、println と stack.peek(); により、スタックの先頭の値である2が出力されます。
次に「=」のキーが押されると、EQUALは数字キーでもCLEARでもないので〔プログラム 4〕のメソッド onKeyPressed で、次の処理が行われます。
その後、println が実行される行はありませんので、最後の行の出力は「2」となります。
∴f=イ:2
〔gについて〕
「/」はクラス OperationKey で4番目に定義された DIVIDE に対応しています。
「8/2/=」の順で実行すると次のように処理されていきます。
∴g=ケ:Error
〔プログラム 5〕の main メソッドの以下を見ると
for (OperationKey key : OperationKey.values())
map.put("+-*/=C".charAt(key.ordinal()), key);
「C」はクラス OperationKey で6番目に定義された CLEAR、「=」は5番目に定義された EQUAL に対応しているとわかります。map.put("+-*/=C".charAt(key.ordinal()), key);
また「C」が処理されるときには、〔プログラム 4〕のメソッド onKeyPressed で、
} else if (key == OperationKey.CLEAR) {
reset();
}
と記述されており、メソッド reset の処理を確認すると、reset();
}
stack.clear();
stack.push(0);
pendingKey = null;
と初期化しています(電卓のキーで言えばAC)。「2*4C2=」は途中に「C」があり初期化を経るので、実行結果は「C」以降の文字列である「2=」と等しくなります。したがって「2=」の動作だけを考えれば足ります。stack.push(0);
pendingKey = null;
まず「2」のキーが押されると、「2」に対応する DIGIT2 を引数としてメソッド onKeyPressed が呼び出され、以下の処理が実行されます。
if (key instanceof DigitKey) {
if (pendingKey == OperationKey.EQUAL) {
reset();
}
key.operateOn(stack);
System.out.println(stack.peek());
}
直前の「C」の処理のメソッド reset により pendingKey は null になっているので、ここでは reset は行われず、key.operateOn(stack); だけが実行されます。reset によりスタックの先頭には 0 が格納されているので、メソッド operateOn によりスタックの先頭が「0×10+2=2」に更新されます。if (pendingKey == OperationKey.EQUAL) {
reset();
}
key.operateOn(stack);
System.out.println(stack.peek());
}
そして、println と stack.peek(); により、スタックの先頭の値である2が出力されます。
次に「=」のキーが押されると、EQUALは数字キーでもCLEARでもないので〔プログラム 4〕のメソッド onKeyPressed で、次の処理が行われます。
if (pendingKey != null) {
pendingKey.operateOn(stack);
}
System.out.println(stack.peek());
pendingKey は null なのでif文の内部は実行されず、println と stack.peek(); により、スタックの先頭の値である2が出力されます。pendingKey.operateOn(stack);
}
System.out.println(stack.peek());
その後、println が実行される行はありませんので、最後の行の出力は「2」となります。
∴f=イ:2
〔gについて〕
「/」はクラス OperationKey で4番目に定義された DIVIDE に対応しています。
「8/2/=」の順で実行すると次のように処理されていきます。
- 8
- 「0×10+8」でスタックの先頭を更新し、8を出力する。
スタックの状態:[8]、pendingKey:null - /
- 次の処理が行われます。if (pendingKey != null) {pendingKey は null なので operateOn は実行されず、スタックの先頭である8を出力します。その後、pendingKey に DIVIDE が格納され、スタックに 0 が積まれます。
pendingKey.operateOn(stack);
}
System.out.println(stack.peek());
pendingKey = key;
if (key != OperationKey.EQUAL) {
stack.push(0);
}
スタックの状態:[8, 0]、pendingKey:DIVIDE - 2
- 「0×10+2」でスタックの先頭を更新し、2を出力する。
スタックの状態:[8, 2]、pendingKey:DIVIDE - /
- pendingKey に DIVIDE が格納されているので operateOn を実行します。スタックから、先頭から2番目の値である 8 を val1 に、スタックの先頭である 2 を val2 に取り出して、メソッド calculate が呼びされます。DIVIDE のときには val1 を val2 で割った値をスタックに積むので「8÷2=4」がスタックの先頭の値となります。
そして、println と stack.peek(); により、スタックの先頭の値である4が出力されます。
その後、pendingKey の値を DIVIDE に更新し(従前のまま)、スタックに 0 を積みます。
スタックの状態:[4, 0]、pendingKey:DIVIDE - =
- pendingKey に DIVIDE が格納されているので operateOn を実行します。スタックから、先頭から2番目の値である 4 を val1 に、スタックの先頭である 0 を val2 に取り出して割り算をします。このとき「4÷0」と0で除算を行うことになるので、ArithmeticException が発生します。この例外は、catch文で次のように処理されます。} catch(ArithmeticException e) {Errorを出力し、電卓を初期化しています。
System.out.println("Error");
reset();
}
∴g=ケ:Error