Существует сладкий маленький класс называется DynamicObjectAdapterFactory
отправленный Heinz Kabutz, который использует дженерик и отражение для адаптации объекта для реализации интерфейса, обеспечивая его с исходным классом, который уже реализует интерфейс.
Используя его, как показано ниже, вы можете обернуть свой INSTANCE
в прокси. Конечно, результирующий объект больше не является enum
, но он делает сохраняет все однотональности enum
. Он также, очевидно, может использовать любой объект для реализации вашего интерфейса.
Я думаю, что это как можно ближе к enum
, расширяющему класс.
Вот несколько тестовых кодов, которые, похоже, работают. Очевидно, что объект больше не является enum
, но поскольку ваша цель была одиночной, это может быть приемлемым.
public class Test {
// To implement this.
public interface Implement {
public void init();
public void connect();
public void disconnect();
public boolean isConnected();
}
// An implementor that does implement.
public static class Implements implements Implement {
@Override
public void init() {
}
@Override
public void connect() {
}
@Override
public void disconnect() {
}
@Override
public boolean isConnected() {
return false;
}
}
// Extend the INSTANCE in this.
public enum Extend {
INSTANCE;
// Hold my adapted version - thus still a singleton.
public final Implement adaptedInstance;
Extend() {
// Use the constructor to adapt the instance.
adaptedInstance = DynamicObjectAdapterFactory.adapt(this, Implement.class, new Implements());
}
}
// Provides an INSTANCE that has been extended by an Implements to implement Implement.
public static Implement getInstance() {
return Extend.INSTANCE.adaptedInstance;
}
public void test() {
System.out.println("Hello");
Implement i = getInstance();
}
public static void main(String args[]) {
new Test().test();
}
}
Вот DynamicObjectAdapterFactory - я подправил его немного от оригинала - Я надеюсь, что доктор Kabutz не возражает.
public class DynamicObjectAdapterFactory {
// Use methods in adaptee unless they exist in target in which case use adapter.
// Implement target in passing.
public static <T> T adapt(final Object adaptee,
final Class<T> target,
final Object adapter) {
return (T) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[]{target},
new InvocationHandler() {
private final String name =
adaptee.getClass().getSimpleName() + "(" + adaptee.toString() + ")"
+ "+" + adapter.getClass().getSimpleName() + "(" + adapter.toString() + ")";
// The methods I wish to adapt.
private Map<MethodIdentifier, Method> adaptedMethods = new HashMap<>();
{
// initializer block - find all methods in adapter object
Method[] methods = adapter.getClass().getDeclaredMethods();
for (Method m : methods) {
// Keep a map of them.
adaptedMethods.put(new MethodIdentifier(m), m);
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// Has it been adapted?
Method otherMethod = adaptedMethods.get(new MethodIdentifier(method));
if (otherMethod != null) {
return otherMethod.invoke(adapter, args);
} else {
return method.invoke(adaptee, args);
}
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
// Really simple. May get more flexible later.
s.append("Adapted: ").append(name);
return s.toString();
}
});
}
private static class MethodIdentifier {
private final String name;
private final Class[] parameters;
public MethodIdentifier(Method m) {
name = m.getName();
parameters = m.getParameterTypes();
}
@Override
public boolean equals(Object o) {
// I am always equal to me.
if (this == o) {
return true;
}
// I cannot be equal to something of a different type.
if (!(o instanceof MethodIdentifier)) {
return false;
}
MethodIdentifier mid = (MethodIdentifier) o;
return name.equals(mid.name) && Arrays.equals(parameters, mid.parameters);
}
@Override
public int hashCode() {
return name.hashCode();
}
}
}
Перечисления не могут распространяться ни на что. Однако вы можете заставить их реализовать общий интерфейс. –