Singleton Factory - 使用 Java 8 实现

Singleton Factory - Implementation using Java 8

我想实现一个通用的单例工厂模式,我将所需对象的 Class 作为参数传递,工厂 class 应该检查映射是否已经创建了一个对象它,如果是,return 地图中的对象。如果没有,创建一个新实例,将其放入地图和 return 实例。

我可以将通用 return 类型作为对象,但我不想在调用 get 实例方法的每个地方都强制转换 returned 对象。

下面是代码:我在 c.cast(instance);

行遇到编译错误

我们不使用 spring/dependency 注入,但尝试实现通用 class 来创建所有单例对象。

public class SingletonFactory {
    public static Map<String,Object> objectFactory = new HashMap<String, Object>();

    public static <T extends Object> T getInstance(Class<?> c){
        String key = c.toString();
        Object instance= objectFactory.get(key);
        if (instance == null) {
            synchronized (c) {
                try {
                    instance = c.newInstance();
                    objectFactory.put(key, instance);
                } catch(IllegalAccessException | InstantiationException e) {
                    throw new RuntimeException("Exception while creating singleton instance for class : "+key+" - Exception Message : "+e);
                }
            }
        }
        return c.cast(instance);
    }
}

首先,我可以指出 <T extends Object> 可以仅替换为 <T>,因为 Java 中涉及泛型的所有内容都必须是对象。

您真正接近的第二部分是 Class<?> c。那就是说你可以传入任何 class 并且它将 return 无论类型 T 是什么。如果您认为 c.cast(instance) 看起来更好,可以将 c.cast(instance) 替换为 (T) instance,但实际上存在差异,此处有更详细的说明:Java Class.cast() vs. cast operator .

最终代码如下所示:

public class SingletonFactory {
    public static Map<String,Object> objectFactory = new HashMap<String, Object>();

    public static <T> T getInstance(Class<T> c){
        synchronized (c) {
            String key = c.toString();
            Object instance= objectFactory.get(key);
            if (instance == null) {
                try {
                    instance = c.newInstance();
                    objectFactory.put(key, instance);
                } catch(IllegalAccessException | InstantiationException e){
                    throw new RuntimeException("Exception while creating singleton instance for class : "+key+" - Exception Message : "+e);
                }
            }
            return c.cast(instance);
            // or
            return (T) instance;
        }
    }
}

此外,如果您真的想要,您可以保留原始代码中的所有内容,并在方法结束时将实例强制转换为 T,它应该可以工作。唯一的问题是您的方法调用看起来像 SingletonFactory.getInstance<Foo>(Foo.class) 而不是 SingletonFactory.getInstance(Foo.class)。这是因为您的原始代码中的 Class<?> 而不是 Class<T>.

编辑:我还更改了代码以更早同步感谢@Khan9797

首先,你需要更早同步,你应该简单地同步方法,否则,你可以在竞争条件下创建额外的实例。

其次,您应该像这样定义方法的泛型:

public static <T> T getInstance(Class<? extends T> c)

首先,getInstance()在创建新实例方面不是线程安全的。当多个线程 运行 同时存在且 variable == nulltrue.

时,您有可能创建给定 class 的多个实例
public class SingletonFactory {
    private static Map<Class, Object> objectHolder = new HashMap<>();

    public <T> T getInstance(Class<T> clazz) {
        Object instance = objectHolder.get(clazz);
        if(instance == null) {
            synchronized (clazz) {
                if(instance == null) {
                    try{
                        instance = clazz.newInstance();
                        objectHolder.put(clazz, instance);
                    } catch (Exception e) {
                        // do some logging and maybe exit the program. Since the it would affect how whole system works.
                    }                            
                }
            }
        }

        return clazz.cast(instance);
    }  
}

但更好的方法是使用急切初始化而不是延迟初始化。我们需要同步临界区的原因是我们在需要时创建这些实例。于是就变成了readers-writers的问题。但是如果我们只做读取过程那么我们就不需要同步因为我们不会修改它的值。如果您知道将要创建并需要访问的所有 classes,我们可以首先初始化它们。这样我们就可以摆脱 synchronized 性能缺陷

public class SingletonFactory {
    private static Map<Class, Object> objectHolder = new HashMap<>();

    private Map<Class, Object> initialize() {
        Map<Class, Object> objectHolder = new HashMap<>();
        // create some objects and put it into Map
        return objectHolder;        

    }           

    public <T> T getInstance(Class<T> clazz) {
        Object instance = objectHolder.get(clazz);            
        return clazz.cast(instance);
    }  
}