How to parameterize a singleton in java

I have the following problem. I have an interface:

public interface Worker<T> { public void start(Class<? extends T> taskClass); } 

and a 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 a singleton instance this way:

 Worker<Thread> listener = Listener.getInstance(); 

is still type safe? when not, how can I use generic in a singleton instance with a safe type?

+3
source share
2 answers

All generic files are deleted at runtime.

This means that you will have only one static class SingletonHolder and only one INSTANCE no matter what the general is.

To get past this, you can have multiple plural single numbers, if that makes sense. You will need to add some kind of registration, so if you try to get one that does not exist yet, create and save it before returning it. Please note that this is not compiled and there may be some problems 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 ...; } } 
+5
source

Just

 public final class Listener implements Worker<Runnable> { private Listener() {} @Override public void start(Class<? extends Runnable> taskClass) { ... } } 

it is clear that any Thread subclass can accept a listener instance.

 Worker<Runnable> worker = Listener.getInstance(); worker.start( MyThread.class ); 

If you want to restrict the type to clarify what works for Thread subclasses, you cannot

 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 can argue that it is clear that Worker<Runnable> also a Worker<Thread> ; error[1] is just a dumb restriction.

One of the advantages of erasing is that we can establish such covariance.

 @SuppressWarnings("unchecked") Worker<Thread> worker = (Worker<Thread>)(Worker) Listener.getInstance(); 

We know that casting is safe for all purposes and purposes.

If this is often necessary, we can move covariance casting to getInstance() for convenience:

 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; 
+1
source

All Articles