Как добавить общие методы для нескольких перечислений Java? (предки абстрактного класса?)

У меня есть несколько перечислений Java как таковых

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

Как вы можете видеть, здесь существует довольно много дубликатов кода. Было бы неплохо, если бы появился способ представить что-то вроде абстрактного общего класса предков. Но java перечисление не может быть присущим. Какой был бы лучший подход? Благодарю.


Изменить: У меня есть версия, похожая на ŁukaszBachman и 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 );
    }

}

Я вижу некоторые рассуждения. Однако это все еще не очень удовлетворительно.

  • Трудно определить интерфейс для lookup(String) из-за другого типа возврата
  • Я понимаю, что lookup(String) не является действительно дублированием, а спецификацией, но я все еще чувствую, что поле m_Name и логика toString() немного избыточны. Мы действительно указываем одну категорию перечисления, и, по моему мнению, это отношения "-a" .

Ответ 1

Наслаждайтесь композицией по наследованию и программированием ради интерфейсов. Поскольку Enums являются классами (не регулярными, но все еще-классами), вы можете создать какое-либо поле, содержащее общую логику, позволить enum реализовать ваш интерфейс и делегировать реализацию в это поле.

Соответствующие фрагменты кода:

Общий интерфейс

public interface MyInterface {

    void someMethod();

}

Логическая реализация

public class MyInterfaceImpl implements MyInterface {

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

}

Первое перечисление

public enum EnumA implements MyInterface {
    ;

    private MyInterface impl = new MyInterfaceImpl();

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

}

Второе перечисление

public enum EnumB implements MyInterface {
    ;

    private MyInterface impl = new MyInterfaceImpl();

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

}

Обратите внимание, что EnumA и EnumB на самом деле не дублируют код, так как это простое делегирование (на мой взгляд). Также обратите внимание, что все красиво склеено с помощью интерфейса.

Ответ 2

Вот как вы можете решить свою проблему с композицией и делегированием. (Я думаю, что это DRY est вы можете получить с Java, для рассматриваемого случая.)

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
  }
}

Ответ 3

Вы можете добиться этого с помощью методов интерфейса Java 8 по умолчанию:

public class test
{
    public static void main (String[] arguments) throws Exception
    {
        X.A.foo ();
        Y.B.foo ();
    }
}

interface MyEnumInterface
{
    String getCommonMessage ();
    String name ();

    default void foo ()
    {
        System.out.println (getCommonMessage () + ", named " + name ());
    }
}

enum X implements MyEnumInterface
{
    A, B;

    @Override
    public String getCommonMessage ()
    {
        return "I'm an X";
    }
}

enum Y implements MyEnumInterface
{
    A, B;

    @Override
    public String getCommonMessage ()
    {
        return "I'm an Y";
    }
}

Обратите внимание, что интерфейс не знает, что он будет реализован перечислениями, поэтому он не может использовать методы Enum на this в методах по умолчанию. Однако вы можете включить эти методы в сам интерфейс (например, с помощью name()), а затем использовать их в обычном режиме. Они будут "реализованы" для вас Enum, когда вы объявите перечисление.

Ответ 4

Как насчет статического вспомогательного класса, который содержит ваши общие функции, вызовите их из ваших методов enum.

Что касается вашего комментария о toString().

public enum MyEnum{

ONE("one");

public MyEnum(String m_Name){
  this.m_Name = m_Name;
}
public String toString(){
  return m_Name;
}
String m_Name;
}

Ответ 5

просто определите общее поведение в первом классе:

public class First {
 public String name() {
  return "my name";
 }
 ...
}

а затем расширьте его в каждом классе:

public SecondClass extends First {
 ...
}