Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】is this code running with jdk21?

2025年09月24日に「Reddit /r/programming」が公開したITニュース「is this code running with jdk21?」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

じゃんけんプログラムのJavaコードが、JDK8やJDK21で実行しても常に「Invalid input.」と表示される問題が発生。開発者はバージョン違いが原因か疑問を投げかける。コード内容からは、入力値の処理方法が原因の可能性を示唆する。

ITニュース解説

このニュース記事では、じゃんけんゲームを実装したJavaコードが提示されており、プログラマーがJDK 8とJDK 21の両方で実行しても「Invalid input. Please run the program again and enter Rock, Paper, or Scissors.」というエラーメッセージが表示され、プログラムが正しく動作しないという問題が提起されている。プログラマーはこれがJDKのバージョンに起因する問題ではないかと考えているようだ。しかし、この問題はJDKのバージョンではなく、提示されたコードの書き方、特に標準入力の扱い方に根本的な原因がある可能性が高い。

まず、提示されたJavaコードの全体像を確認する。このプログラムは、プレイヤー1とプレイヤー2の二人でじゃんけんをするシンプルなコンソールアプリケーションである。import java.util.Scanner;という行は、ユーザーからのキーボード入力を受け取るために必要なScannerクラスをプログラムで利用可能にする宣言である。public class RockPaperScissors { ... }は、プログラムの処理をまとめるクラスの定義であり、public static void main(String[] args) { ... }はそのクラス内でプログラムが最初に実行を開始する場所(エントリーポイント)を示している。

プログラムの実行が始まると、まずScanner scanner0 = new Scanner(System.in);Scanner scanner1 = new Scanner(System.in);という二つの行がある。ここで重要なのは、Scannerオブジェクトが二つ作られていること、そして両方がSystem.inという同じもの(標準入力、つまりキーボードからの入力)を読み込むように設定されている点だ。通常、キーボードからの入力を受け取るためのScannerオブジェクトはプログラム中で一つあれば十分である。

次に、System.out.printを使って「Rock Paper Scissors Game. Please enter Rock, Paper, or Scissors.」というゲームの開始メッセージと、「Player1 >> 」というプレイヤー1への入力プロンプトが表示される。String player1 = scanner0.next();という行で、プレイヤー1が入力した文字列がplayer1という変数に格納される。同様に、「Player2 >> 」というプロンプトが表示され、String player2 = scanner1.next();でプレイヤー2の入力がplayer2変数に格納される。

その後、switch (player1)文を使って、プレイヤー1の入力に応じて勝敗を判定するロジックが続く。プレイヤー1が「Scissors」「Paper」「Rock」のいずれかを選択した場合のそれぞれについて、プレイヤー2の入力と比較して「Player1 wins!」「Player2 wins!」「It's a tie.」のいずれかを表示する。しかし、このプログラムが常に表示すると言われているエラーメッセージ「Invalid input. Please run the program again and enter Rock, Paper, or Scissors.」は、default:の後に書かれている。これは、player1に入力された文字列が、「Scissors」「Paper」「Rock」のいずれとも完全に一致しなかった場合に実行される部分だ。

なぜこのdefaultブロックが常に実行されるのだろうか。プログラマーは正しい文字列を入力していると主張しているため、単純な入力ミス(大文字小文字の間違いや余分なスペースなど)だけが原因とは考えにくい。問題の核心は、複数のScannerオブジェクトを同じSystem.inに対して作成していること、そしてそれらのScannerオブジェクトをclose()している点にある。

System.inはプログラム実行中にキーボードからの入力という唯一のリソースを指す。Scannerオブジェクトは、この入力ストリームを読みやすくするための道具である。Scanner scanner0 = new Scanner(System.in);System.inを読み取るScannerが作られ、scanner0.next();でプレイヤー1の入力を読み取る。ここまでは問題ない。しかし、その後にScanner scanner1 = new Scanner(System.in);同じSystem.inに対して別のScannerオブジェクトが作られている。この時点で、二つのScannerが同じ入力元を監視している状態になる。

さらに、問題のコードの末尾にはscanner0.close();scanner1.close();という行がある。Scannerクラスのclose()メソッドは、そのScannerが使用していた基礎となる入力ストリームも閉じてしまう可能性がある。System.inはプログラム全体で共有されるべき標準入力ストリームであり、これを途中で閉じてしまうと、それ以降、そのプログラム内ではキーボードからの入力を一切受け付けられなくなる。具体的に何が起こるかというと、player1の入力がscanner0.next()で読み取られた後、scanner0.close()が実行されることでSystem.inが閉じられてしまう状況が考えられる。そうなると、次にplayer2 = scanner1.next();が実行される際、scanner1は既に閉じられたSystem.inから入力を受け取ろうとすることになり、これは失敗する。失敗した場合、player2には何らかの空文字列や予期せぬ値が設定されたり、例外が発生したりする可能性がある。player2が正常に読み取られなかった場合、switch文の条件判定も正しく行われなくなり、結果としてplayer1の値もおかしく解釈されてdefaultブロックが実行される、という流れが考えられる。

もう一つの可能性としては、ユーザーが入力する際に、例えば「Rock 」のように最後にスペースを入れてしまったり、「rock」のように小文字で入力してしまったりした場合も、Javaの文字列比較(equals()メソッド)は厳密なため、「Rock」とは一致せずdefaultブロックが実行されてしまう。この可能性も捨てきれないが、プログラマーが「あらゆる手を試した」と言っていることから、単純な入力ミスだけが原因ではないと推測される。

プログラマーはJDK 8とJDK 21の両方で試していることから、バージョンに原因があると考えているようだが、これは間違いである。Scannerクラスの基本的な挙動、System.inの扱い、およびequals()メソッドによる文字列比較の動作は、JDK 8とJDK 21の間で、この種のコードに影響を与えるほど大きく変更されてはいない。この問題はJavaのバージョンに依存するものではなく、コードの論理的な設計と、標準入力ストリームという共有リソースの扱いの誤りに起因するものである。

この問題を解決するための最も簡単な方法は、Scannerオブジェクトを一つだけ使用することである。一つのScannerオブジェクトでプレイヤー1とプレイヤー2の入力を順番に受け取れば、System.inの競合や不適切なclose()による問題は発生しない。修正コードのイメージは次のようになるだろう。まずScanner scanner = new Scanner(System.in);と一つだけScannerを作成し、String player1 = scanner.next();String player2 = scanner.next();のように、その一つのscannerオブジェクトを使い回す。そして、scanner.close();は、原則としてプログラムの終了時、つまりmainメソッドの最後に一度だけ呼び出すべきである。ただし、System.inはJVMプロセス全体で共有されるリソースなので、特に意識して閉じない、または確実に閉じられる安全な方法(例えばtry-with-resources文を使用する)で扱うのが望ましい。初学者のうちは、System.inをラップしたScannerは閉じずにプログラムの終了に任せるという選択肢もある。

さらに、入力文字列の大文字小文字を区別しないようにするためには、player2.equals("Scissors")のような比較をplayer2.equalsIgnoreCase("Scissors")に変更すると良い。これにより、「rock」と入力しても「Rock」として扱われるようになるため、ユーザーフレンドリーなプログラムになる。

提示されたじゃんけんゲームのコードが常に「Invalid input」のエラーを出す問題は、JDKのバージョンアップに起因するものではなく、標準入力(System.in)を扱うScannerオブジェクトの不適切な使用方法が原因である。具体的には、同じSystem.inに対して複数のScannerオブジェクトを作成し、さらにそれらを途中でclose()しようとすることで、後続の入力読み込みが失敗している可能性が高い。プログラムは、キーボードからの入力を受け取るためにScannerオブジェクトを一つだけ作成し、それを適切に使い回すことで、この問題を解決できる。また、入力の文字列比較を大文字小文字を区別しないequalsIgnoreCase()メソッドにすることで、より堅牢なプログラムにすることができる。これは、プログラミングにおける基本的なリソース管理と、予期せぬ挙動を避けるための設計の重要性を示している良い例である。

関連コンテンツ

関連IT用語

関連ITニュース