I have next problem. I have an interface:
public interface Worker<T> {
public void start(Class<? extends T> taskClass);
}
and singleton implementation of this interface:
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();
}
}
My question is, can I get the singleton instance by this way:
Worker<Thread> listener = Listener.getInstance();
is still type-safe? when not, how can I use generic in singleton instance to by type-safe?
All generics are stripped at runtime.
This means you're only going to have one static class SingletonHolder and only one INSTANCE no matter what the generic is.
To get past this, you could have 'multiple singletons' if that makes sense. You would have to add some kind of registration, so if you tried to get one that didn't exist yet, create and store it before returning it. Note this isn't compiled, and there may be some issues with it, but the basic approach is still the same.
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 ...;
}
}
Simply
public final class Listener implements Worker<Runnable> {
private Listener() {}
@Override
public void start(Class<? extends Runnable> taskClass) { ... }
}
clearly, the listener instance can accept any Thread subclass.
Worker<Runnable> worker = Listener.getInstance();
worker.start( MyThread.class );
If you want to restrict the type, to clarify that the worker is for Thread subclasses, you can't
Worker<Thread> worker = Listener.getInstance(); // error[1]
but you can
Worker<? super Thread> worker = Listener.getInstance();
worker.start( MyThread.class ); // works for all Thread subclasses.
worker.start( SomeRunnable.class ); // error; only for Thread subclasses.
If we hate wildcards (we do), we may want to argue that clearly a Worker<Runnable> is also a Worker<Thread>; error[1] is just silly restriction.
One of the benefit of erasure is that, we can establish this kind of covariance.
@SuppressWarnings("unchecked")
Worker<Thread> worker = (Worker<Thread>)(Worker) Listener.getInstance();
We know the cast is safe for all intents and purposes.
If this is needed frequently, we can move the covariance casting to getInstance() for easy use:
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With