snippetjavaMinor
Can we now `implement Singleton` using Java 8?
Viewed 0 times
canimplementjavanowsingletonusing
Problem
After my success with extending enum - I thought I would try to make
My aim was to create a class which is a
Can anyone see any ways of simplifying it further? I would like to aim for as little as possible in the
I would also like to see a way of forcing, or even pre-defining the
Here's the
```
/**
* A classic Multiton making use of lambdas to delay the create.
*
*
* @author OldCurmudgeon
* @param - The type that can create.
*/
public class Multiton {
/**
* The storage.
*
* Store only Object because they must all be different types.
*/
private final ConcurrentMap multitons = new ConcurrentHashMap<>();
/**
* The keys must be capable of creating their values.
*/
public interface Creator {
public abstract Object create();
}
/**
* The getter.
*
* @param - 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 get(final K key, Class 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 test
Singleton creation as simple as possible since Java-8 makes Multitons 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 - The type that can create.
*/
public class Multiton {
/**
* The storage.
*
* Store only Object because they must all be different types.
*/
private final ConcurrentMap multitons = new ConcurrentHashMap<>();
/**
* The keys must be capable of creating their values.
*/
public interface Creator {
public abstract Object create();
}
/**
* The getter.
*
* @param - 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 get(final K key, Class 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 test
Solution
ConcurrentMap
As was pointed out in the comments: in the Multiton.get() method:
There's no reason to have the double-check. The computeIfAbsent already does that:
Generics
I have taken, and played with your code. It became apparent that there is no reason to have a generic type on the Multiton class. You only really use it as
Consider the following Multiton:
Of particular importance, note:
Note that I can't see a convenient way to use a
Then, whenever you have a need for a Singleton:
and then:
This can be encapsulated in to an abstract
The Singleton class can then be used in various ways, here's an example:
```
public class TestSingleton extends Singleton {
private static final Multiton.Creator KEY = new Multiton.SuppliedCreator<>(TestSingleton.class, TestSingleton::new);
public static final TestSingleton getInstance() {
As was pointed out in the comments: in the Multiton.get() method:
public V get(final K key, Class 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);
}There's no reason to have the double-check. The computeIfAbsent already does that:
public V get(final K key, Class type) {
return type.cast(multitons.computeIfAbsent(key, k -> k.create()));
}Generics
I have taken, and played with your code. It became apparent that there is no reason to have a generic type on the Multiton class. You only really use it as
Multiton, and that's the broadest sense of the Generics anyway. The more I played with it, though, the more I realized that your logic is in the wrong place. The right place would be to have more in the Multiton, and to get rid of the Singleton class entirely.... There is no reason for the Singleton interface. It creates a lot of ugliness anyway, because the interface cannot have private static members, so the Multiton instance needs to be non-private, making it a resource leak.Consider the following Multiton:
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier;
/**
* A classic Multiton making use of lambdas to delay the create.
*
*
* @author OldCurmudgeon
* @param - The type that can create.
*/
public class Multiton {
/**
* The keys must be capable of creating their values.
*/
public interface Creator {
public Object create();
public V cast(Object value);
}
public static abstract class TypedCreator implements Creator {
private final Class tclass;
public TypedCreator(Class tclass) {
this.tclass = tclass;
}
@Override
public final A cast(Object value) {
return tclass.cast(value);
}
}
public static final class SuppliedCreator extends TypedCreator {
private final Supplier supplier;
public SuppliedCreator(Class tclass, Supplier supplier) {
super(tclass);
this.supplier = supplier;
}
@Override
public Object create() {
return supplier.get();
}
}
/**
* The storage.
*
* Store only Object because they must all be different types.
*/
private final ConcurrentMap, Object> multitons = new ConcurrentHashMap<>();
/**
* The getter.
*
* @param - The type of the value that should be returned.
* @param key - The unique key behind which the value is to be stored.
* @return - The value stored (and perhaps created) behind the key.
*/
public > V get(final C key) {
return key.cast(multitons.computeIfAbsent(key, k -> k.create()));
}
}Of particular importance, note:
- there's no generic type for the Multiton. This is expected. All it needs is the public
get()method, and the generics for that method are determined by the call arguments.... which is point 2....
- the generic type of the values are declared on the Creator (which is also the key). In other words, each key knows what the type of the value should be. This means that the generic type is 'recorded' at create time, rather than retrieve time. There's no need to pass a class in to the
get()call since the class is actually part of theCreator.
- I created 2 'helper' classes that are Creator instances. These classes
TypedCreatorandSuppliedCreatorare simpler ways to actually create anonymous, or Java-8 based Creators.
Note that I can't see a convenient way to use a
Singleton interface. The interface depends on a shared instance of the Multiton, so as a result, there needs to be some static data in the Singleton. As a consequence, an interface is the wrong language structure to use. An abstract class may be better. Still, the usage examples for the Multiton above are simple (in some common 'utility' class, or somewhere shared):public static final Multiton MULTITON = new Multiton();Then, whenever you have a need for a Singleton:
public static final Multiton.Creator HARDWORK = new Multiton.SuppliedCreator<>(String.class, () -> initializeHardWork());and then:
String hardwork = MULTITON.get(HARDWORK);This can be encapsulated in to an abstract
Singleton class as:public abstract class Singleton {
protected static final Multiton MULTITON = new Multiton();
}The Singleton class can then be used in various ways, here's an example:
```
public class TestSingleton extends Singleton {
private static final Multiton.Creator KEY = new Multiton.SuppliedCreator<>(TestSingleton.class, TestSingleton::new);
public static final TestSingleton getInstance() {
Code Snippets
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);
}public <V> V get(final K key, Class<V> type) {
return type.cast(multitons.computeIfAbsent(key, k -> k.create()));
}import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier;
/**
* A classic Multiton making use of lambdas to delay the create.
*
*
* @author OldCurmudgeon
* @param <K> - The type that can create.
*/
public class Multiton {
/**
* The keys must be capable of creating their values.
*/
public interface Creator<V> {
public Object create();
public V cast(Object value);
}
public static abstract class TypedCreator<A> implements Creator<A> {
private final Class<A> tclass;
public TypedCreator(Class<A> tclass) {
this.tclass = tclass;
}
@Override
public final A cast(Object value) {
return tclass.cast(value);
}
}
public static final class SuppliedCreator<A> extends TypedCreator<A> {
private final Supplier<A> supplier;
public SuppliedCreator(Class<A> tclass, Supplier<A> supplier) {
super(tclass);
this.supplier = supplier;
}
@Override
public Object create() {
return supplier.get();
}
}
/**
* The storage.
*
* Store only Object because they must all be different types.
*/
private final ConcurrentMap<Creator<?>, Object> multitons = new ConcurrentHashMap<>();
/**
* 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.
* @return - The value stored (and perhaps created) behind the key.
*/
public <V, C extends Multiton.Creator<V>> V get(final C key) {
return key.cast(multitons.computeIfAbsent(key, k -> k.create()));
}
}public static final Multiton MULTITON = new Multiton();public static final Multiton.Creator<String> HARDWORK = new Multiton.SuppliedCreator<>(String.class, () -> initializeHardWork());Context
StackExchange Code Review Q#69594, answer score: 8
Revisions (0)
No revisions yet.