Java の enum Singleton が「キモい」と言われたので隠蔽してみた

## 概要

`Animal animal = Dog.INSTANCE`の`Dog.INSTANCE`を隠蔽する試作 4 つ

## 1. enum をパッケージプライベートにする

### 実装

```java:Dog.java
package animal;

enum Dog implements Animal {
    INSTANCE
}
```

```java:Animal.java
package animal;

public interface Animal {
    static Animal getDog() {
        return Dog.INSTANCE;
    }
}
```

### 備考

- インスタンスの取得: `Animal animal = Animal.getDog()`
- `Animal.getDog() instanceof Dog`: コンパイルエラー

## 2. enum をクラスの内部クラスにする

### 実装

```java:Cat.java
import animal.Animal;

public class Cat {
    private enum CatImpl implements Animal {
        INSTANCE
    }

    public static Animal getInstance() {
        return CatImpl.INSTANCE;
    }

    private Cat() {
    }
}
```

### 備考

- インスタンスの取得: `Animal animal = Cat.getInstance()`
- `Cat.getInstance() instanceof Cat`: false

## 3. enum をインタフェースの内部クラスにする

### 実装

```java:Bird.java
import animal.Animal;

public interface Bird extends Animal {
    class Cage {
        private enum BirdImpl implements Bird {
            INSTANCE
        }

        private Cage() {
        }
    }

    static Bird getInstance() {
        return Cage.BirdImpl.INSTANCE;
    }
}
```

### 備考

- インスタンスの取得: `Bird bird = Bird.getInstance()`
- `Bird.getInstance() instanceof Bird`: true
- Bird インタフェースの実装クラスは無数に定義できるので Bird 型のインスタンスは 1 つではない

## 4. 3 のインスタンスを 1 つに制限する

### 実装

```java:Fox.java
package fox;

import animal.Animal;

public sealed interface Fox extends Animal permits FoxImpl {
    static Fox getInstance() {
        return FoxImpl.INSTANCE;
    }
}

enum FoxImpl implements Fox {
    INSTANCE
}
```

### 備考

- インスタンスの取得: `Fox fox = Fox.getInstance()`
- `Fox.getInstance() instanceof Fox`: true
- Fox 型インスタンスは FoxImpl のインスタンス 1 つのみ

## 実行結果

```java:Main.java
import animal.Animal;
import fox.Fox;

public class Main {
    public static void main(String[] args) {
        Animal dog = Animal.getDog();

        System.out.print("dog.getClass(): ");
        System.out.println(dog.getClass());

        System.out.print("dog instanceof Animal: ");
        System.out.println(dog instanceof Animal);

        // System.out.println(dog instanceof Dog);

        System.out.println();

        Animal cat = Cat.getInstance();

        System.out.print("cat.getClass(): ");
        System.out.println(cat.getClass());

        System.out.print("cat instanceof Animal: ");
        System.out.println(cat instanceof Animal);

        System.out.print("cat instanceof Cat: ");
        System.out.println(cat instanceof Cat);

        System.out.println();

        Bird bird = Bird.getInstance();

        System.out.print("bird.getClass(): ");
        System.out.println(bird.getClass());

        System.out.print("bird instanceof Animal: ");
        System.out.println(bird instanceof Animal);

        System.out.print("bird instanceof Bird: ");
        System.out.println(bird instanceof Bird);

        System.out.println();

        Fox fox = Fox.getInstance();

        System.out.print("fox.getClass(): ");
        System.out.println(fox.getClass());

        System.out.print("fox instanceof Animal: ");
        System.out.println(fox instanceof Animal);

        System.out.print("fox instanceof Fox: ");
        System.out.println(fox instanceof Fox);
    }
}
```

```text:標準出力
dog.getClass(): class animal.Dog
dog instanceof Animal: true

cat.getClass(): class Cat$CatImpl
cat instanceof Animal: true
cat instanceof Cat: false

bird.getClass(): class Bird$Cage$BirdImpl
bird instanceof Animal: true
bird instanceof Bird: true

fox.getClass(): class fox.FoxImpl
fox instanceof Animal: true
fox instanceof Fox: true
```

## 参考文献

### Effective Java 第 3 版

- 項目 1 コンストラクタの代わりに static ファクトリメソッドを検討する

- 項目 3 private のコンストラクタか enum 型でシングルトン特性を強制する

## 【付録 A】 enum Singleton に直接ファクトリメソッドを作る

### 実装

```java:Singleton.java
public enum Singleton {
    INSTANCE;

    public static Singleton getInstance() {
        return INSTANCE;
    }
}
```

### 備考

- インスタンスの取得: `Singleton singleton = Singleton.getInstance()`  
  ただし、`Singleton singleton = Singleton.INSTANCE`も可

## 【付録 B】 全ソースコード

<https://gist.github.com/NeetworkEngineerSato/e2026a76cfaa327ef51c1b0a39e14cf0>