概要
Animal animal = Dog.INSTANCE
のDog.INSTANCE
を隠蔽する試作 4 つ
1. enum をパッケージプライベートにする
実装
Dog.java
package animal;
enum Dog implements Animal {
INSTANCE
}
Animal.java
package animal;
public interface Animal {
static Animal getDog() {
return Dog.INSTANCE;
}
}
備考
- インスタンスの取得:
Animal animal = Animal.getDog()
-
Animal.getDog() instanceof Dog
: コンパイルエラー
2. enum をクラスの内部クラスにする
実装
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 をインタフェースの内部クラスにする
実装
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 つに制限する
実装
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 つのみ
実行結果
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);
}
}
標準出力
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 に直接ファクトリメソッドを作る
実装
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