【问题标题】:How to add common methods for a few Java enums? (abstract class ancestor?)如何为几个Java枚举添加常用方法? (抽象类祖先?)
【发布时间】:2012-06-25 05:04:05
【问题描述】:

我有一些这样的 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 );
    }

}

我看到了一些道理。但是,它仍然不是很令人满意。

  1. lookup(String) 的接口很难定义,因为返回类型不同
  2. 我可以理解lookup(String) 并不是真正的重复,而是一种规范,但我仍然觉得 m_Name 字段和 toString() 逻辑有点多余。我们确实指定了一类枚举,在我看来,这似乎是 "is-a" 关系。

【问题讨论】:

  • 如果继承不起作用,委托会解决问题。
  • 你不能继承静态的东西,所以即使枚举支持实现继承,也无济于事。
  • buc,你能说得更具体一点吗?谢谢。
  • 您是否考虑过使用valueOf 代替所有查找内容?它似乎是大部分重复的代码,无论如何你都可以在任何枚举中免费获得它。
  • 所以你有所有这些逻辑只是让值名称全部大写,而它们的内容只有一个大写字母?我可能会更改值的命名以匹配它们所需的内容,从而消除所有这些逻辑。我知道你在问一个一般性的问题,所以这只是一个评论。这只是正式实践(此处的命名约定)引入其他不必要的逻辑的情况之一。放弃形式可以产生更好的代码,而不必使用一些花哨的模式。

标签: java oop inheritance enums


【解决方案1】:

组合优于继承为了接口而编程。由于枚举是类(不是常规的,但仍然是类),您可以创建一些包含共享逻辑的字段,让枚举实现您的接口并将实现委托给该字段。

相关代码sn-ps:

共享界面

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

}

请注意EnumAEnumB 并不是真正的代码重复,因为这是简单的委托(在我看来是有效的)。另请注意,所有内容都通过使用界面很好地粘合在一起。

【讨论】:

  • 这是一个不错的解决方案。你也可以让枚举返回一个MyInterface,而是用getMyInterface方法实现一个MyInterfaceProvider。这样,您也可以摆脱委托方法的重复。这也可以与Jeremy's solution that uses default methods 结合使用。
【解决方案2】:

以下是解决组合和委派问题的方法。 (我认为这是您可以使用 Java 获得的 DRYest,就手头的情况而言。)

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

【讨论】:

  • 谢谢我混合了这两种解决方案,我只需调用此代码作为我的静态方法来从内部属性创建枚举:return new EnumEnhancer&lt;&gt;(values()).fromKey(key);
【解决方案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";
    }
}

请注意,接口不知道它将由枚举实现,因此它不能在默认方法中使用this 上的Enum 方法。但是,您可以在接口本身中包含这些方法(就像我对name() 所做的那样),然后正常使用它们。当您声明枚举时,它们将由 Enum 为您“实施”。

【讨论】:

  • 很高兴看到 java 发展成为一个老问题的某种解决方案。
【解决方案4】:

一个包含常用函数的静态帮助类怎么样,从你的枚举方法中调用它们。

关于您对 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;
}

【讨论】:

  • lookup(String) 方法可能没问题。但是你会如何处理m_name 字段和toString()
  • 关于 toString() 方法,为什么不将其作为 ENUM 本身的实例方法,而不是您定义的每个单独 ENUM 的一部分。
  • 我认为扩展 ENUM 类不能与内置枚举类型一起使用
【解决方案5】:

只需在 First class 中定义您的常见行为:

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

然后在每个类中扩展它:

public SecondClass extends First {
 ...
}

【讨论】:

  • 我不相信枚举可以扩展一个类——它们只能实现接口。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-15
  • 2019-10-01
相关资源
最近更新 更多