2

I've created two enum classes as singleton:

public enum A {
    INSTANCE;

    public void init(param p1, param p2) {
    }

    public void connect() {
    }

    public void disconnect() {
    }

    public bool isConnected() {
    }
}

public enum B {
    INSTANCE;

    public void init(param p1) {
    }

    public void connect() {
    }

    public void disconnect() {
    }

    public bool isConnected() {
    }
}

As you can see both enum classes are very similar so I was wondering if I should create some kind of base abstract class/enum or interface and then have these two enums extend or implement from it.

UPDATE 1: I'd like to put some shared member variables on the base class

UPDATE 2: Should I just change the way I'm defining the singleton?

Roman C
  • 49,761
  • 33
  • 66
  • 176

4 Answers4

2

As per java enum tutorial

All enums implicitly extend java.lang.Enum. Since Java does not support multiple inheritance, an enum cannot extend anything else.

Here is interesting SO discussion related to this topic.

Community
  • 1
  • 1
kosa
  • 65,990
  • 13
  • 130
  • 167
2

As Nambari stated you can't have an enum extend anything. However what they neglected to say is you CAN have an enum implement an interface, which is done as with a class using the implements keyword. I've done this at work and it's very useful in the right situation! There's an example here: http://javahowto.blogspot.co.uk/2008/04/java-enum-examples.html

eldris
  • 205
  • 1
  • 5
0

There is a sweet little class called a DynamicObjectAdapterFactory posted by Heinz Kabutz which uses generics and reflection to adapt an object to implement an interface by providing it with a source class that already implements the interface.

Using it like below you can wrap your INSTANCE in a proxy. Of course the resulting object is no longer an enum but it does retain all of the singletonness of the enum I think. It also, obviously - can use any object to implement your interface.

I think this is as close as you will get to an enum extending a class.

Here's some test code that seems to work. Obviously the object is no longer an enum but as your aim was a singleton this may be acceptable.

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();
  }
}

Here's the DynamicObjectAdapterFactory - I've tweaked it a little from the original - I hope Dr. Kabutz does not object.

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();
    }
  }
}
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
0

You can use the abstract class below for singleton instead of enum.

public abstract class AbstractSingleton {

    private static Map<String, AbstractSingleton> registryMap = new HashMap<String, AbstractSingleton>();

    AbstractSingleton() throws SingletonException {
        String clazzName = this.getClass().getName();
        if (registryMap.containsKey(clazzName)) {
            throw new SingletonException("Cannot construct instance for class " + clazzName + ", since an instance already exists!");
        } else {
            synchronized (registryMap) {
                if (registryMap.containsKey(clazzName)) {
                    throw new SingletonException("Cannot construct instance for class " + clazzName + ", since an instance already exists!");
                } else {
                    registryMap.put(clazzName, this);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz) throws InstantiationException, IllegalAccessException {
        String clazzName = clazz.getName();
        if (!registryMap.containsKey(clazzName)) {
            synchronized (registryMap) {
                if (!registryMap.containsKey(clazzName)) {
                    T instance = clazz.newInstance();
                    return instance;
                }
            }
        }
        return (T) registryMap.get(clazzName);
    }

    public static AbstractSingleton getInstance(final String clazzName)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        if (!registryMap.containsKey(clazzName)) {
            Class<? extends AbstractSingleton> clazz = Class.forName(clazzName).asSubclass(AbstractSingleton.class);
            synchronized (registryMap) {
                if (!registryMap.containsKey(clazzName)) {
                    AbstractSingleton instance = clazz.newInstance();
                    return instance;
                }
            }
        }
        return registryMap.get(clazzName);
    }

    @SuppressWarnings("unchecked")
    public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz, Class<?>[] parameterTypes, Object[] initargs)
            throws SecurityException, NoSuchMethodException, IllegalArgumentException,
            InvocationTargetException, InstantiationException, IllegalAccessException {
        String clazzName = clazz.getName();
        if (!registryMap.containsKey(clazzName)) {
            synchronized (registryMap) {
                if (!registryMap.containsKey(clazzName)) {
                    Constructor<T> constructor = clazz.getConstructor(parameterTypes);
                    T instance = constructor.newInstance(initargs);
                    return instance;
                }
            }
        }
        return (T) registryMap.get(clazzName);
    }

    static class SingletonException extends Exception {
        private static final long serialVersionUID = -8633183690442262445L;

        private SingletonException(String message) {
            super(message);
        }
    }
}

From: https://www.cnblogs.com/wang9192/p/3975748.html

Zhou Hongbo
  • 1,297
  • 13
  • 25