[memo] Java の FizzBuzz コードゴルフ

# Java の FizzBuzz コードゴルフ

* Java の各バージョンでの FizzBuzz の最短コードと思われるものを、とりあえずメモ
* 基本的に java と javac はセットで使う
* 普通のコードゴルフ同様、標準エラー出力や終了コードは無視する
* 基本的にコードの最後に改行はつけない（文字数を減らすため）

## Java1.4.2 (105char)

```java:A.java
class A{static{for(int i=0;i++<100;)System.out.println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i+"":"":"Buzz"));}}
```

* Java1.4.2 の時代は、三項演算子は同じ型でないとコンパイルエラーになる
* `class` に `main` メソッドがなくてもスタティックイニシャライザが動いてしまう。ただしエラーメッセージは出る

```
$ java -version
java version "1.4.2_19"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_19-b04)
Java HotSpot(TM) Client VM (build 1.4.2_19-b04, mixed mode)
$ javac -version
javac: -version は無効なフラグです。
使い方: javac <options> <source files>
使用可能なオプションには次のものがあります。
  -g                        すべてのデバッグ情報を生成する
  -g:none                   デバッグ情報を生成しない
  -g:{lines,vars,source}    いくつかのデバッグ情報だけを生成する
  -nowarn                   警告を発生させない
  -verbose                  コンパイラの動作についてメッセージを出力する
  -deprecation              推奨されない API が使用されているソースの位置を出力する
  -classpath <path>         ユーザクラスファイルを検索する位置を指定する
  -sourcepath <path>        入力ソースファイルを検索する位置を指定する
  -bootclasspath <path>     ブートストラップクラスファイルの位置を置き換える
  -extdirs <dirs>           インストール済み拡張機能の位置を置き換える
  -d <directory>            生成されたクラスファイルを格納する位置を指定する
  -encoding <encoding>      ソースファイルが使用する文字エンコーディングを指定する
  -source <release>         指定されたリリースとソースの互換性を保つ
  -target <release>         特定の VM バージョン用のクラスファイルを生成する
  -help                     標準オプションの概要を出力する

$ echo -n 'class A{static{for(int i=0;i++<100;)System.out.println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i:"":"Buzz"));}}' > A.java
$ javac A.java
A.java:1: ? に対する互換性のない型 : どちらも他方のサブタイプではありません。
2 番目のオペランド : int
3 番目のオペランド : java.lang.String
class A{static{for(int i=0;i++<100;)System.out.println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i:"":"Buzz"));}}
                                                                                     ^
A.java:1: ? に対する互換性のない型 : どちらも他方のサブタイプではありません。
2 番目のオペランド : int
3 番目のオペランド : java.lang.String
class A{static{for(int i=0;i++<100;)System.out.println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i:"":"Buzz"));}}
                                                                               ^
エラー 2 個
$ echo -n 'class A{static{for(int i=0;i++<100;)System.out.println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i+"":"":"Buzz"));}}' > A.java
$ wc -c
105 A.java
$ javac A.java
$ java A
1
2
Fizz
4
Buzz
（略）
97
98
Fizz
Buzz
Exception in thread "main" java.lang.NoSuchMethodError: main
```

* Java1.4.2 の時代には javac に `-version` オプションがないらしい

## Java5～Java6 (96char)

```java:A.java
enum A{B;int i;{for(;i++<100;)System.out.println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i:"":"Buzz"));}}
```

* java での最短記録の時期
* 実行時にエラーメッセージが出るが `main` メソッド無しでも動いてしまう
* Java5 で追加された `enum` でさらに短く

```
$ java -version
java version "1.5.0_22"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_22-b03)
Java HotSpot(TM) Client VM (build 1.5.0_22-b03, mixed mode, sharing)
$ javac -version
javac 1.5.0_22
javac: ソースファイルがありません。
使い方: javac <options> <source files>
使用可能なオプションには次のものがあります。
  -g                         すべてのデバッグ情報を生成する
  -g:none                    デバッグ情報を生成しない
  -g:{lines,vars,source}     いくつかのデバッグ情報だけを生成する
  -nowarn                    警告を発生させない
  -verbose                   コンパイラの動作についてメッセージを出力する
  -deprecation               推奨されない API が使用されているソースの位置を出力する
  -classpath <path>          ユーザクラスファイルを検索する位置を指定する
  -cp <path>                 ユーザクラスファイルを検索する位置を指定する
  -sourcepath <path>         入力ソースファイルを検索する位置を指定する
  -bootclasspath <path>      ブートストラップクラスファイルの位置を置き換える
  -extdirs <dirs>            インストール済み拡張機能の位置を置き換える
  -endorseddirs <dirs>       推奨規格パスの位置を置き換える
  -d <directory>             生成されたクラスファイルを格納する位置を指定する
  -encoding <encoding>       ソースファイルが使用する文字エンコーディングを指定する
  -source <release>          指定されたリリースとソースの互換性を保つ
  -target <release>          特定の VM バージョン用のクラスファイルを生成する
  -version                   バージョン情報
  -help                      標準オプションの概要を出力する
  -X                         非標準オプションの概要を出力する
  -J<flag>                   <flag> を実行システムに直接渡す

$ echo -n 'enum A{B;int i;{for(;i++<100;)System.out.println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i:"":"Buzz"));}}' > A.java
$ wc -c A.java
96 A.java
$ javac A.java
$ java A
1
2
Fizz
4
Buzz
（略）
97
98
Fizz
Buzz
Exception in thread "main" java.lang.NoSuchMethodError: main
```

* Java5 の時代は javac に `-version` が付いたけど、ソースファイルを省略すると怒られる

## Java7 (130char)

```java:A.java
class A{public static void main(String[]a){for(int i=0;i++<100;)System.out.println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i:"":"Buzz"));}}
```

* Java6 までは `main` 無しでも（エラーを出しつつ）スタティックイニシャライザで動いていたけど、Java7 からは動かなくなってしまった
* 一番長く、一番素直なコードの時期

```
$ java -version
java version "1.7.0_80"
Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
Java HotSpot(TM) Client VM (build 24.80-b11, mixed mode, sharing)
$ javac -version
javac 1.7.0_80
$ echo -n 'enum A{B;int i;{for(;i++<100;)System.out.println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i:"":"Buzz"));}}' > A.java
$ javac A.java
$ java A
エラー: メイン・メソッドがクラスAで見つかりません。次のようにメイン・メソッドを定義してください。
   public static void main(String[] args)
$ echo -n 'class A{public static void main(String[]a){for(int i=0;i++<100;)System.out.println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i:"":"Buzz"));}}' > A.java
$ wc -c A.java
130 A.java
$ javac A.java
$ java A
1
2
Fizz
4
Buzz
（略）
97
98
Fizz
Buzz
```

## Java8～20 (127char)

```java:A.java
interface A{static void main(String[]a){for(int i=0;i++<100;)System.out.println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i:"":"Buzz"));}}
```

* `interface` に `main` メソッドが書けるようになって `public` が不要になったのでちょこっと短く

```
$ java -version
java version "1.8.0_212"
Java(TM) SE Runtime Environment (build 1.8.0_212-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.212-b10, mixed mode)
$ javac -version
javac 1.8.0_212
$ echo -n 'interface A{static void main(String[]a){for(int i=0;i++<100;)System.out.println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i:"":"Buzz"));}}' > A.java
$ wc -c A.java
127 A.java
$ javac A.java
$ java A
1
2
Fizz
4
Buzz
（略）
97
98
Fizz
Buzz
```

## Java21 (97char?)仮

2023-09 リリース予定の Java21 では [JEP445](https://openjdk.org/jeps/445) がプレビューとして取り込まれることになっているため、以下のように FizzBuzz が実装できるようです。

```java:Main.java
int i;void main(){for(;i++<100;)System.out.println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i:"":"Buzz"));}
```

Java21 時点では実行時にプレビュー機能を有効にする指定をしないと実行できません。

```
$ javac --release 21 --enable-preview Main.java
$ java --enable-preview Main
```

あるいは（[JEP330](https://openjdk.org/jeps/330) を使って、ソースを直接実行）

```
$ java --source 21 --enable-preview Main.java
```

## Jshell (83char)

```java
int i;for(;i++<100;)System.out.println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i:"":"Buzz"))
```

* Java9 から使えるようになった Jshell 版

```shell-session
$ jshell --version
jshell 11.0.3
$ echo -n 'int i;for(;i++<100;)System.out.println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i:"":"Buzz"))' | jshell -
1
2
Fizz
4
Buzz
（略）
97
98
Fizz
Buzz
```

## Jshell PRINTING 版 (72char)

```java
int i;for(;i++<100;)println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i:"":"Buzz"))
```

* jshell のコードゴルフのレギュレーション詳細を知りませんが、なんとなく PRINTING  版
* 単に `System.out.` を取り除いただけ

```shell-session
$ echo -n 'int i;for(;i++<100;)println((i%3>0?"":"Fizz")+(i%5>0?i%3>0?i:"":"Buzz"))' | jshell PRINTING -
1
2
Fizz
4
Buzz
（略）
97
98
Fizz
Buzz
```