我有下一个问题。我有一个界面:
public interface Worker<T> {
public void start(Class<? extends T> taskClass);
}
和此接口的单例实现:
public final class Listener<T extends Runnable> implements Worker<T> {
private Listener() {}
@Override
public void start(Class<? extends T> taskClass) { ... }
public static Listener getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
public static final Listener INSTANCE = new Listener();
}
}
我的问题是,我可以通过这种方式获得单例实例:
Worker<Thread> listener = Listener.getInstance();
仍然是类型安全的吗?什么时候不能,如何在单例实例中使用泛型?通过类型安全?
答案 0 :(得分:5)
所有泛型都在运行时被删除。
这意味着无论通用是什么,您只会拥有一个static class SingletonHolder
而只有一个INSTANCE
。
为了解决这个问题,如果有意义的话,你可以拥有'多个单身'。您必须添加某种注册,因此如果您尝试获取一个尚未存在的注册,请在返回之前创建并存储它。请注意,这不是编译的,可能存在一些问题,但基本方法仍然相同。
public final class Listener<T extends Runnable> implements Worker<T> {
private Listener() {}
@Override
public void start(Class<? extends T> taskClass) { ... }
public static Listener getInstance(Class<T> clazz) {
return SingletonHolder.INSTANCE.get(clazz);
}
private static class SingletonHolder {
private static final Map<Class<? extends Runnable>,Listener<? extends Runnable> INSTANCE = new ...;
}
}
答案 1 :(得分:1)
简单地
public final class Listener implements Worker<Runnable> {
private Listener() {}
@Override
public void start(Class<? extends Runnable> taskClass) { ... }
}
显然,侦听器实例可以接受任何Thread子类。
Worker<Runnable> worker = Listener.getInstance();
worker.start( MyThread.class );
如果要限制类型,为了澄清worker是Thread子类,你不能
Worker<Thread> worker = Listener.getInstance(); // error[1]
但你可以
Worker<? super Thread> worker = Listener.getInstance();
worker.start( MyThread.class ); // works for all Thread subclasses.
worker.start( SomeRunnable.class ); // error; only for Thread subclasses.
如果我们讨厌通配符(我们这样做),我们可能想要明确Worker<Runnable>
也是Worker<Thread>
; error[1]
只是愚蠢的限制。
擦除的好处之一是,我们可以建立这种协方差。
@SuppressWarnings("unchecked")
Worker<Thread> worker = (Worker<Thread>)(Worker) Listener.getInstance();
我们知道演员阵容对所有意图和目的都是安全的。
如果经常需要这样做,我们可以将协方差转换为getInstance()
以便于使用:
Worker<Thread> worker = Listener.getInstance();
public final class Listener implements Worker<Runnable>
static Listener instance = new Listener();
// it is safe to cast Worker<A> to any Worker<B>
// as long as B is subtype of A
@SuppressWarnings("unchecked")
static public <T extends Runnable> Worker<T> getInstance()
return (Worker<T>)(Worker)instance;