After my success with extending enum - I thought I would try to make Singleton
creation as simple as possible since Java-8 makes Multiton
s comparatively easy.
My aim was to create a class which is a Singleton
solely by the simple fact that it implements a Singleton
interface. Unfortunately there is a little baggage required but it doesn't look too bad. I don't think I would use it in a real setting as it still doesn't provide the level of simplicity I am looking for but as a prototype it seems to work.
Can anyone see any ways of simplifying it further? I would like to aim for as little as possible in the TestSingleton
class.
I would also like to see a way of forcing, or even pre-defining the getInstance()
method but that looks impossible to me.
Here's the Multiton
it uses:
/**
* A classic Multiton making use of lambdas to delay the create.
*
*
* @author OldCurmudgeon
* @param <K> - The type that can create.
*/
public class Multiton<K extends Multiton.Creator> {
/**
* The storage.
*
* Store only Object because they must all be different types.
*/
private final ConcurrentMap<K, Object> multitons = new ConcurrentHashMap<>();
/**
* The keys must be capable of creating their values.
*/
public interface Creator {
public abstract Object create();
}
/**
* The getter.
*
* @param <V> - The type of the value that should be returned.
* @param key - The unique key behind which the value is to be stored.
* @param type - The class of the value returned.
* @return - The value stored (and perhaps created) behind the key.
*/
public <V> V get(final K key, Class<V> type) {
// Has it run yet?
Object o = multitons.get(key);
// A null value means not yet.
if (o == null) {
// Use a lambda to only do the create if it is still absent.
o = multitons.computeIfAbsent(key, k -> k.create());
}
return type.cast(o);
}
// Some simple testing.
enum E implements Creator {
A {
@Override
public String create() {
return "Face";
}
},
B {
@Override
public Integer create() {
return 0xFace;
}
},
C {
@Override
public Void create() {
return null;
}
};
}
public static void main(String args[]) {
try {
Multiton<E> m = new Multiton<>();
String face1 = m.get(E.A, String.class);
Integer face2 = m.get(E.B, Integer.class);
System.out.println("Face1: " + face1 + " Face2: " + Integer.toHexString(face2));
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
}
And my attempt at a interface Singleton
, along with TestSingleton
to demonstrate:
/**
* Implement this and you're a singleton.
*
* @author OldCurmudgeon
* @param <T> - The type we are a Singleton of.
*/
public interface Singleton<T extends Singleton<T>> {
/**
* A Creator command to create the singleton.
*
* @param <T> - The actual Singleton type.
*/
class Creator<T extends Singleton<T>> implements Multiton.Creator {
final Supplier<Singleton<T>> singletonConstructor;
final Class<T> singletonClass;
public Creator(Supplier<Singleton<T>> constructor, Class<T> clazz) {
this.singletonConstructor = constructor;
this.singletonClass = clazz;
}
@Override
public Singleton<T> create() {
return singletonConstructor.get();
}
}
/**
* Use one Multiton to keep track of all singletons.
*/
static final Multiton<Creator> multitons = new Multiton<>();
/**
* The getInstance must create the object if need be.
*
* @param <T> - The type of the singleton created.
* @param creator - A Creator for the class.
* @return - the Singleton.
*/
static <T extends Singleton<T>> T getInstance(Creator<T> creator) {
return multitons.get(creator, creator.singletonClass);
}
/**
* A test singleton.
*/
class TestSingleton implements Singleton<TestSingleton> {
/**
* My constructor should be called only once.
*/
public TestSingleton() {
System.out.println("Creating a TestSingleton!");
}
/**
* A Lightweight creator that can therefore be static.
*
* This must be static final as it is the key in the Multiton map.
*/
private static final Creator creator = new Creator(TestSingleton::new, TestSingleton.class);
/**
* Implement the expected getInstance() - sadly not forced.
*
* @return - The same TestSingleton for ever.
*/
public static TestSingleton getInstance() {
return Singleton.<TestSingleton>getInstance(creator);
}
}
public static void main(String args[]) {
try {
TestSingleton s1 = TestSingleton.getInstance();
TestSingleton s2 = TestSingleton.getInstance();
if (s1 == s2) {
System.out.println("OK!");
}
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
}
if (o == null) o = multitons.computeIfAbsent())
at all? Just return the result of.computeIfAbsent()
. – fge Nov 12 '14 at 18:05TestSingleton
beprivate
? – Christian Hujer Nov 26 '14 at 18:00