public static void mainがなくなるって話、結局どうなったの?
2年前に以下の記事が話題になりました。
Javaという言語のHello, world!は、難解ではない主要な高級言語の中ではワーストレベルで前提だらけかつ冗長なものである・・・という状態が長く続いていたJava界隈に衝撃が走りました。
その内容は、Java 21で、Javaのプログラムを書くときにいちいちpublic static void main(String[] args)という長ったらしい呪文を書かなくてもよくなる・・・というものでした。具体的には、Hello, world!が次のように書けるようになりました。
void main() {
System.out.println("Hello, world!");
}
しかし、かなり致命的なただし書きがありました。それは、あくまでもプレビュー機能にとどまるという点です。
プレビュー機能は、まだ仕様が確定しておらず、ものによっては取り消される運命にある場合があります。実際、文字列テンプレート(JEP 430)という文字列リテラル中に変数の内容を埋め込めるようになる機能がプレビュー段階の中Java 23で打ち切りに遭っています。またプレビュー機能の仕様も確定ではなく、次のバージョンで変更が入る場合もあります。
さらに、プレビュー機能は単純に使うのに余計なオプションを渡さなければならず、手間がかかります。実行のたびにJavaから「お前のコードはプレビュー機能使ってるぞ、わかってるな」といちいち釘を差されます。そのため、本番用のコードでは相変わらずpublic static void mainを書かなければなりませんでした。
Java 25でのプレビュー卒業により確定した新Hello, world
本機能は幸い途中で削除されることはなく、Java 25のJEP-512で、プレビューを卒業することになりました。すなわち、特別な設定なしで誰でも脱public static void mainできるということになります。
また、println・readlnについては、IO.println・IO.readlnと書かなければいけなくなり、この仕様で確定しました。IOというクラスはJava 23から属していましたが、所属パッケージがjava.ioからjava.langに移動し、23・24でクラス名を省略可能な自動インポートされていたのがなくなりました。
そのため、25で仕様確定した新しいHello, world!は次のようなります。
void main() {
IO.println("Hello, world!");
}
標準入力を交えた例は次のようになります。
void main() {
var name = IO.readln("名前を入力してください:");
IO.println("ハローワールド、" + name + "さん");
}
かなりシンプルになり、初心者に説明しないといけないことが減ったのがわかると思います。
なお、従来のpublic static void mainも冗長ですが廃止になったわけではなく引き続き有効です。
モジュールインポート宣言によるimport文削減
Hello, worldでは必須でないので分けましたが、上のmain簡略化と並行して策定が進められていた新構文があります。モジュールインポート宣言です。
通常、ArrayListとSecureRandomを同時に使いたいとなると、ズボラに書く場合は次のようになります。
import java.util.*;
import java.security.*;
このように、ちょっとクラスを使いたいだけなのに、最悪の場合import文のオンパレードになってしまいます。
幸いなことに、これらは同じモジュールというものに所属しています。モジュールというのは、普通のimport文でインポートできる単位のパッケージをいくつかまとめた上位単位です。
このjava.util・java.securityが所属しているモジュール名は、java.baseです。
このモジュールに属しているパッケージは次のページで確認できます。(21時点)よく使うパッケージが収録されていることがわかります。
このモジュールに属しているパッケージ全部をインポートする場合、次の1行で終わりです。
import module java.base;
なお、クラス名が被った場合、使う方をmoduleでないimport文でインポートし直します。
JShell・上の簡易main構文を利用しているファイル内では、
import module java.base;
が暗黙的に宣言されているものとされています。すなわち、ArrayList・SecureRandom・Pattern・Matcher・BigDecimalの全てが初期状態で使えます。
| クラス | 従来main ファイル |
mainなし ファイル |
新main ファイル |
JShell |
|---|---|---|---|---|
| java.langパッケージ内 (例: String) |
○ | ○ | ○ | ○ |
| java.lang外かつ java.baseモジュール内 (例: ArrayList) |
× | × | ○ | ○ |
| java.base外 (例: JFrame) |
× | × | × | × |
従来形式だと上1文でカバーできますが、それすら不要になるのは初心者に教える際に余計な説明を減らせるので助かりますね。
この新機能は、JEP-511で策定され、プレビュー卒業が宣言されました。
実際の例は、すぐ下のコードを見てください。
関数やクラスを追加する場合の書き方
これもHello worldでは関係ない話なので章を分けました。この新main構文を使った場合、同じファイルにグローバル変数・関数・別クラスを追加することができます。
String greetingString = "ハローワールド";
void main() {
IO.println(greeting());
IO.println(new SecureRandom().nextInt(4) == 0 ? "あたり" : "はずれ");
var point = new Point(3.0, 4.0);
IO.println(point);
IO.println(point.norm());
}
String greeting() {
return greetingString;
}
record Point(double x, double y) {
public double norm() {
return Math.sqrt(x * x + y * y);
}
public String toString() {
return "(" + x + ", " + y + ")";
}
}
※record = 読み取り専用特化のクラスの亜種
※SecureRandomは上の自動モジュールインポート宣言により、手動でimportを書かなくても最初から利用可能
いつから正式解禁?
Java 25のリリース(GA)予定は、9月16日です。(8月30日時点)
どうやって試す?
無事予定の狂いもなくリリースされました。(日本では翌日朝)
OSが提供している(主にLinux)のでもない場合、個人的にはEclipse Temurinという派生版がオススメです。
Windowsでは、sudoを有効化している場合、次のコマンドでインストール完了です。
sudo winget install EclipseAdoptium.Temurin.25.JDK
それ以外でSDKMAN!を使っている場合は次のコマンドでインストール完了です。
sdk install java 25-tem
Webサイトから直接ダウンロードしたい場合はここを参照してください。
Oracle公式サイトからダウンロードできるものは、初リリースから3年以内であっても社外向け本番はNGです。
(a) internally use the unmodified Programs for the purposes of developing, testing, prototyping and demonstrating your applications, and running the Program for Your own personal use or internal business operations;
初稿当時
Java 25は7月時点ではまだリリースされていません。https://jdk.java.net/25/ からアーリーアクセス版をダウンロードして実行する必要があります。ZIPまたはtarballを解凍して実行してください。もちろん安定版ではないので自己責任です。バグを見つけたら https://bugreport.java.com/ に報告するのが関の山です。バグ一覧はhttps://bugs.openjdk.org/secure/Dashboard.jspa から探せます。(2件修正しないと登録できません)
上のプログラムをHelloWorld.javaに保存した場合は、
java HelloWorld.java
ですぐ実行できます。今どきわざわざjavacする必要はありません。
日本語を含めたい場合、Java 21以降(厳密には18以降)はWindows含めてUTF-8で保存してください。
また、jshellで、JShellというREPLを起動できます。Javaで何かサクッと試したい場合の心強い味方です。
注意
Java 25のJShellのIO.readlnには即Ctrl + Dするとエラーが出るバグがあります。修正は26(LTSは29)以降となります。本来はnullになるべきです。
jwebserver -d ./path/to/my/siteでpath/to/my/site内のHTMLページをプレビューできます。Java 25以降は-dオプションに相対パスを指定できるようになり、使い勝手が向上しました。
(余談)Java 22~24の脱public static void main
煩わしいpublic static void mainを書かずにJavaのHello, worldなど教育用プログラムを書く仕組みを定義する一連のJEPは、Java 22以降も25に至るまで仕様の改訂を続けていました。
詳しくは、以下の公式ページをご覧ください。
大きな変更点は、Java 23でprintlnのSystem.out.を省略して書けるようになり、readlnという標準入力を簡単に取得できるメソッドが追加された点です。
ズブのJava初心者に標準出力について説明するのは酷な話なので、大きな改善です。また、Javaで標準入力から入力値を行儀よく取得するのは割と面倒だったため、これも対初心者で親切になりました。
Java 17~22の書き方(簡易・Scanner使用の場合):
// Scannerの第2引数は25以降では別の書き方をすべき
try (var sc = new Scanner(System.in, System.console().charset()) {
System.out.println(sc.nextLine());
}
Java 23・24での書き方:
println(readln());
Java24以前のモジュールインポート宣言
モジュールインポート宣言も、これらのJEPと並行して、Java 23・24でプレビュー機能としてのお試し期間がありました。
各バージョンのJEPページは次のリンクです。
Java 25では、24から特に変更なくプレビュー卒業となりました。
まとめ
- Java 21の時点ではまだプレビュー版だったので一般人には関係なかった
- Java 25でプレビュー卒業したので晴れて気軽に脱パブリックスタティックヴォイドメインできるようになった
- 21→25で
printlnもより簡単に書けるようになり、入力処理も劇的に簡単になった - もちろん従来のパブリックスタティックヴォイドメインも引き続き有効
- ついでに
ArrayListなどでいちいちimport文を書かなければいけない問題が大幅緩和
個人的感想
「JavaはHello worldが冗長すぎてクソ・トラウマ」という評判とはおさらばになれそうで本当に良かったと思います。
Java講座に関わっている人は大変だと思いますが、これを加味した内容に改訂してもらいたいところです。8、下手したらそれより前の内容のままろくに改訂を行ってきていないところもチラホラ目立つのであまり期待していませんが。