如何在Java中创建一个通用的单例类?

6
我想在Java中创建一个通用的单例类,可以通过传递类名作为方法参数来获取该类的单例对象。就像下面的代码一样:请帮忙实现。
public final class Listener<T extends Object>  {
    private Listener() {
    }

    public static <T> Listener<?> getInstance(Class<T> clazz) {
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        public static final Listener INSTANCE = new Listener();
        // private static final Map<Class<? extends Runnable>,Listener<? extends
        // Runnable> INSTANCE = new ...;
    }
}

你为什么需要它是泛型的?在你的例子中,你并没有以任何方式使用T。 - Nicola Musatti
这是一个关于Java中如何参数化泛型单例的问题。 - Ahmet Karakaya
这样做不会违反单例模式的整个理念吗(即只有一个类实例)? - G-Man
你到底想要实现什么?如果它是一个真正的单例,它就不能真正地通用,因为你必须在创建时选择类型参数。或者你想要每个T类型的一个实例? - Heiko Schmitz
1
顺便说一下,泛型单例确实是有意义的——只要它不会获取或提供其泛型参数的任何实例。Collections.empty[List|Map|Set]() 返回单例实例,如果尝试添加或获取任何元素,则失败。emptyList(等等)方法具有未经检查的转换,将单例转换为 List<T>(其他集合接口也是如此)。 - yshavit
更具体地说:只要它假定它们是对象,它就可以接受“T”;只要它们始终为空,它就可以返回“T”。 - yshavit
5个回答

7
创建一个只有一个值的枚举,例如INSTANCE。这是一种快速创建单例的方法。
如果我理解您的意思正确,您想让您的单例实现一些通用接口?类似于:
interface ListenerInterface<T> {
    void handleEvent(T event);
}

在这种情况下,您可以使用未经检查的强制转换以通用方式返回枚举作为单例。只要您的单例不实际使用该实例(或者仅将其视为对象),这是安全的。
public enum MySingleton implements ListenerInterface<Object> {
    INSTANCE;

    @SuppressWarnings("unchecked")
    public static <T> ListenerInterface<T> getListener() {
        return (ListenerInterface<T>) INSTANCE;
    }

    public void handleEvent(Object event) {
        System.out.println("I am a singleton. An event happened: " + event);
    }

}

如果接口是生产者而不是消费者 -- 也就是说,它返回一个T -- 那么你只能安全地返回null。否则,当其他人尝试使用他们认为是Foo但实际上只是Object的T时,他们会得到ClassCastException。


+1,我也不明白为什么会有人踩,这是一个微不足道而简单的好解决方案。此外,这里还有一个关于枚举作为单例实现的讨论的好链接:http://closingbraces.net/2011/07/04/enum-as-singleton/。 - Michael

3
这本来应该是一条评论,但由于字数限制,我认为更好的做法是回答这个问题。
深入思考一下,创建一个通用的单例类真的可能吗?
单例类既不能被实例化,也不应该被克隆。要让任何类具有这种行为,您将需要将所有构造函数设为私有,并重写克隆方法以抛出异常。如果您打算更改所有类以实现此行为,那么您的单例方式是否真的是通用的呢?
如果我对问题的解释有误,请原谅我。

1

你可以像这样做,但在我看来不可靠:

public class SimpleSingleton {
private Map<String, Object> counter = new HashMap<String, Object>();

public <T> T getInstance(Class<T> clazz) throws IllegalAccessException, InstantiationException {
    T singleton = (T) counter.get(clazz.getName());
    if (singleton == null) {
        singleton = clazz.newInstance();
        counter.put(clazz.getName(), singleton);
    }
    return singleton;
}

}


1
我的解决方案是这样设计的:
public interface IListener<T extends Object>  {
    void handle(T event);
}

public final class Listener implements IListener<Object> {
    private static Listener instance = new Listener();

    private Listener() {
    }

    public static Listener getInstance() {
        return instance;
    }

    @Override
    public void handle(Object event) {
        //TODO Do stuff
    }
}

0

我不确定我完全理解你的问题,但如果你想将单例集成到通用层次结构中,你需要有点小聪明(就像我们在this topic中所做的那样):

interface Listener<T>{
    // ...
}

@SuppressWarnings("unchecked")
final class SingletonListener {

    private SingletonListener() {
        assert false : "Uninstantiable";
    }

    public static <T> Listener<T> instance() {
        return SingletonListenerInstance.INSTANCE;
    }

    // Note - implements Listener without specifying type parameters!
    private enum SingletonListenerInstance implements Listener {
        INSTANCE;
        // Insert listener methods here
    }
}


// Usage:
Listener<Pony> ponyListener = SingletonListener.instance();

那么,如果这个实现接收类型为T的参数,或者期望返回值(或与之相关的值)的类型为T,它能做些什么呢?嗯,不多,但也不是什么都没有!下面是一个更详细的版本,包括示例实现:

interface Listener<T> {
    void receive(T something);

    T process(T something);

    T produce();

    List<T> produceMany();
}

@SuppressWarnings("unchecked")
final class SingletonListener {

    private SingletonListener() {
        assert false : "Uninstantiable";
    }

    public static <T> Listener<T> instance() {
        return SingletonListenerInstance.INSTANCE;
    }

    // Note - implements Listener without specifying type parameters!
    private enum SingletonListenerInstance implements Listener {
        INSTANCE;
        // Insert listener methods here

        @Override
        public void receive(Object something) {
            // We can log what we receive because we know everything is an Object
            // and so has a toString method; we can also use other Object methods,
            // do reflection (or just ignore it and leave a no-op implementation)
            System.out.println("Received " + something);
        }

        @Override
        public Object process(Object something) {
            // We can return something we know to be of the right type, which in our
            // case typically is arguments we've received
            return something;
        }

        @Override
        public Object produce() {
            // Null is a valid value for all types
            return null;
        }

        @Override
        public List produceMany() {
            // Don't need any instances of the generic type to create an empty list
            return Collections.EMPTY_LIST;
        }
    }
}

1
这不应该是 final class SingletonListener<T> { 吗? - ehartwell
这段代码无法编译,即使你修改它让它能够编译通过,也无法与包含泛型参数或返回类型的Listener<T>一起使用。因此,它只能与Listener一起使用,而不能是Listener<T>。这不是一个通用解决方案。 - user1677663
@user1677663 谢谢指出无法编译的问题,已经修复了。至于方法能做什么,原来实际上有一些可以做的事情!当然,大多数情况下只对哑巴/空操作实现有用,而且你必须跟踪清楚每个东西的作用,否则客户端代码会产生令人惊讶的“ClassCastException”,但是在这方面有用的边角案例是可以被构想出来的。 - gustafc
1
感谢您充分阐述了这种方法,让人们能够看清他们实际上正在采取的方式! - user1677663

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接