Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add common methods for a few Java enums? (abstract class ancestor?)

I have a few Java enums as such

public enum Aggregation
{
    MORTGAGE( "Mortgage" ),
    POOLS( "Pools" ),
    PORTFOLIO( "Portfolio" );

    private Aggregation( final String name )
    {
        m_Name = name;
    }
    private String m_Name;
    static Map< String, Aggregation > c_LOOKUP =
        new HashMap< String, Aggregation >();
    static {
        for (Aggregation agg:values()){
            c_LOOKUP.put(agg.m_Name,agg);
        }
    }

    public Aggregation lookup(String name){
        return c_LOOKUP.get( name );
    }

    @Override
    public String toString()
    {
        return m_Name;
    }
}

public enum Interval
{
    MONTHLY( "Monthly" ),
    QUARTLY( "Quartly" ),
    SEMIANNUALLY( "SemiAnnually" ),
    ANNUALLY("Annually");

    private Interval( final String name )
    {
        m_Name = name;
    }
    private String m_Name;
    static Map< String, Interval > c_LOOKUP =
        new HashMap< String, Interval >();
    static {
        for (Interval agg:values()){
            c_LOOKUP.put(agg.m_Name,agg);
        }
    }

    public Interval lookup(String name){
        return c_LOOKUP.get( name );
    }

    @Override
    public String toString()
    {
        return m_Name;
    }
}

As you can see, there are quite some code duplication here. It would be nice if there is a way to introduce something like an abstract common ancestor class. But java enum cannot inherent. What would be the best approach? Thanks.


Edit: I have work out a version similar to ŁukaszBachman and missingfacktor

static public enum Aggregation
{
    MORTGAGE( "Mortgage" ),
    POOLS( "Pools" ),
    PORTFOLIO( "Portfolio" );

    private final String m_Name;

    final static private ReverseDictionary< Aggregation > c_DICTIONARY =
        new  ReverseDictionary< Aggregation >( Aggregation.class );

    static public Aggregation lookup( final String name )
    {
        return c_DICTIONARY.lookup( name );
    }

    private Aggregation( final String name )
    {
        m_Name = name;
    }

    @Override
    public String toString()
    {
        return m_Name;
    }
}

static public enum Interval
{
    MONTHLY( "Monthly" ),
    QUARTLY( "Quartly" ),
    SEMIANNUALLY( "SemiAnnually" ),
    ANNUALLY( "Annually" );

    private final String m_Name;
    final static private ReverseDictionary< Interval > c_DICTIONARY =
        new ReverseDictionary< Interval >( Interval.class );

    static public Interval lookup( final String name )
    {
        return c_DICTIONARY.lookup( name );
    }

    private Interval( final String name )
    {
        m_Name = name;
    }

    @Override
    public String toString()
    {
        return m_Name;
    }
}


static public class ReverseDictionary< E extends Enum< E >>
{
    Map< String, E > c_LOOKUP = new HashMap< String, E >();

    public ReverseDictionary( final Class< E > enumClass )
    {
        for( final E agg : EnumSet.allOf( enumClass ) )
        {
            c_LOOKUP.put( agg.toString(), agg );
        }
    }

    public E lookup( final String name )
    {
        return c_LOOKUP.get( name );
    }

}

I see some reasoning. However, it is still not very satisfactory.

  1. It is hard to define the interface for lookup(String) because of the different return type
  2. I can appreciate that the lookup(String) is not really duplication but a specification, but I am still feel that m_Name field and the toString() logic is a bit redundant. We are really specifying one category of enum, and it seems to be "is-a" relationship in my opinion.
like image 921
zggame Avatar asked Jun 22 '12 20:06

zggame


People also ask

Can you add methods to enums?

Enum Class in Java An enum class can include methods and fields just like regular classes. When we create an enum class, the compiler will create instances (objects) of each enum constants.

Can you put methods in an enum Java?

The enum class body can include methods and other fields. The compiler automatically adds some special methods when it creates an enum. For example, they have a static values method that returns an array containing all of the values of the enum in the order they are declared.

Can you use abstract methods in enum?

Yes, you can define abstract methods in an enum declaration if and only if all enum values have custom class bodies with implementations of those methods (i.e. no concrete enum value may be lacking an implementation).

Can enum implement abstract class?

Enum cannot extend any class in java,the reason is, by default Enum extends abstract base class java.


2 Answers

Favor composition over inheritance and programming for the sake of interfaces. Since Enums are classes (not regular, but still - classes) you can create some field containing shared logic, let the enum implement you interface and delegate implementation to this field.

Relevant code snippets:

Shared interface

public interface MyInterface {

    void someMethod();

}

Logic implementation

public class MyInterfaceImpl implements MyInterface {

    public void someMethod() {
        System.out.println("Do smth...");
    }

}

First enum

public enum EnumA implements MyInterface {
    ;

    private MyInterface impl = new MyInterfaceImpl();

    public void someMethod() {
        impl.someMethod();
    }

}

Second enum

public enum EnumB implements MyInterface {
    ;

    private MyInterface impl = new MyInterfaceImpl();

    public void someMethod() {
        impl.someMethod();
    }

}

Please do note that EnumA and EnumB are not really code duplication, since that is plain delegation (valid, in my opinion). Also please note that everything is nicely glued together by using interface.

like image 190
ŁukaszBachman Avatar answered Oct 24 '22 17:10

ŁukaszBachman


Here is how you can solve your problem with composition and delegation. (I think this is the DRYest you can get with Java, for the case in hand.)

import java.util.*;

interface HasName {
  public String getName();
}

class EnumEnhancer<E extends Enum<E> & HasName> {
  private Map<String, E> lookup;

  public EnumEnhancer(E... values) {
    lookup = new HashMap<String, E>();
    for (E e : values) {
      lookup.put(e.getName(), e);
    }
  }

  public E lookup(String name) {
    return lookup.get(name);
  }

  public String toString(E e) {
    return e.getName();
  }
}

enum Color implements HasName { // This is interface inheritance.
  RED("red"), GREEN("green"), BLUE("blue");

  // This is composition. 
  private static final EnumEnhancer<Color> enhancer = 
    new EnumEnhancer<Color>(values());

  private String name;

  private Color(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }

  // This is delegation.
  public String toString() {
    return enhancer.toString(this);
  }

  // This too is delegation.     
  public static Color lookup(String name) {
    return enhancer.lookup(name);
  }
}

class Main {
  public static void main(String[] args) {
    System.out.println(Color.lookup("blue")); // prints blue
  }
}
like image 35
missingfaktor Avatar answered Oct 24 '22 19:10

missingfaktor