Singleton を破壊する

なんとなく気になったので、Singleton を破壊するための TIPS を余談として提示してみます。

結局クラスが一意であることを利用してクラスを1対1に結びついた単一のオブジェクトを作ってるだけなんだから

証明を書くには余白が足りない - 西尾泰和のはてなダイアリー

結局、クラスが一意じゃなければ、Singleton は破壊できちゃうんですよね。
今回破壊する Singleton はこういうクラスです。いたってシンプル。

public class Singleton {
    private static final Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() { return instance; }
}

これを、こういうコードからロードしてみちゃったりします。だいぶ乱暴なことをしますね。

import java.io.*;
class test {
    public static void main(String[] args) throws Exception {
        System.out.println(newInstance() == newInstance());
    }
    private static Object newInstance() throws Exception {
        File file = new File("Singleton.class");
        final byte[] data = new byte[(int)file.length()];
        new FileInputStream(file).read(data, 0, data.length);
        return new ClassLoader() {
            public Class<?> destroySingleton() {
                return defineClass("Singleton", data, 0, data.length);
            }
        }.destroySingleton().getMethod("getInstance").invoke(null);
    }
}

結果は "false"
defineClass のたびに、新しい Singleton クラスが返ってきます。
素人にはお勧めできない。
微妙にクラスが一意という条件は満たしていますが、1つのクラスファイルから複数個の Class をロードできる、という例でした。

追記

上記コードを

-    private static Object newInstance() throws Exception {
+    private static Singleton newInstance() throws Exception {

-        return new ClassLoader() {
+        return (Singleton) new ClassLoader() {

のように変更すると、もちろんコンパイルは通るんですが、実行すると

Exception in thread "main" java.lang.ClassCastException: Singleton cannot be cast to Singleton
        at test.newInstance(test.java:10)
        at test.main(test.java:4)

という、世にも奇妙な例外を吐き出してきます。
やっぱり素人にはお勧めできない。