简体   繁体   English

Java枚举:实现通用方法,避免重复

[英]Java enums: implementing common methods, avoiding duplication

Let's say that I have two differents enumeration 假设我有两个不同的枚举

public enum SomeEnumClass {

    private static  final SomeEnumClass[]   mValues = SomeEnumClass .values();
    ONE(1), TWO(2), THREE(3);

}

public enum OtherEnumClass {
    private static  final OtherEnumClass[]  mValues = OtherEnumClass .values();
    Monday(1), Tuesday(2), Wednesday(3), Thrusday(4), Friday(5), Saturday(6), Sunday(7)

}

The enum have in common the type of data they carry (here, an int), and differ in their name and number of possible values. 枚举的共同点是它们携带的数据类型(此处为int),并且名称和可能值的数量不同。

For each of those enum, I have several method to implements, which are strictly identical. 对于每个枚举,我都有几种实现方法,它们是完全相同的。 Example: 例:

        public static OtherEnumClass getCell(int index)
    {
        if (index < OtherEnumClass .mValues.length )
        {
            return OtherEnumClass .mValues[index];              
        }
        throw new IllegalArgumentException("Invalid " + OtherEnumClass .class.getSimpleName() + " value: " + index);
    }

I am trying to find a way to avoid duplication of those methods, like I would do with abstract classes. 我试图找到一种避免重复这些方法的方法,就像我对抽象类所做的那样。 But so far I am coming with nothing. 但是到目前为止,我一无所获。

We are using java 1.6 and cannot upgrade for now. 我们正在使用Java 1.6,目前无法升级。 Any help is appreciated. 任何帮助表示赞赏。 thanks. 谢谢。

You can do this: 你可以这样做:

public enum SomeEnumClass {

    ONE, TWO, THREE;

}

public enum OtherEnumClass {

    Monday, Tuesday, Wednesday, Thrusday, Friday, Saturday, Sunday

}

public static <E extends Enum> E getEnumItem(Class<E> type, int index){
    E[] values = type.getEnumConstants();
    if (index >= 0 && index < values.length){
        return values[index];
    } else {
        throw new IllegalArgumentException("...");
    }
}

public static void main(String[] args) {
    System.out.println(getEnum(SomeEnumClass.class, 0));
    System.out.println(getEnum(OtherEnumClass.class, 3));
    System.out.println(getEnum(SomeEnumClass.class, 2));
    System.out.println(getEnum(OtherEnumClass.class, 6));
}

It prints: 它打印:

ONE
Thrusday
THREE
Sunday

EDITED: This is a similar idea to @dasblinkenlight 编辑:这与@dasblinkenlight类似

public enum SomeEnumClass {

    ONE, TWO, THREE;

    public static SomeEnumClass getCell(int index) {
        return Utility.getEnumItem(SomeEnumClass.class, index);
    }
}

public enum OtherEnumClass {

    Monday, Tuesday, Wednesday, Thrusday, Friday, Saturday, Sunday;

    public static OtherEnumClass getCell(int index) {
        return Utility.getEnumItem(OtherEnumClass.class, index);
    }
}

public static class Utility {

    public static <E extends Enum> E getEnumItem(Class<E> type, int index) {
        E[] values = type.getEnumConstants();
        if (index >= 0 && index < values.length) {
            return values[index];
        } else {
            throw new IllegalArgumentException("...");
        }
    }
}

public static void main(String[] args) {
    System.out.println(Utility.getEnumItem(SomeEnumClass.class, 0));
    System.out.println(Utility.getEnumItem(OtherEnumClass.class, 3));
    System.out.println(Utility.getEnumItem(SomeEnumClass.class, 2));
    System.out.println(Utility.getEnumItem(OtherEnumClass.class, 6));
}

You can wrap your implementations into a generic helper class, and use it in all your implementations. 您可以将实现包装到通用帮助器类中,并在所有实现中使用它。 Unfortunately, you would have to copy the calls into the helper; 不幸的是,您将不得不将呼叫复制到助手中。 Default methods of Java 8 address this problem, but you cannot leverage them since you are limited to Java 6. Java 8的默认方法解决了此问题,但是由于限于Java 6,因此无法利用它们。

// Helper owns the static members that you used to add to your enums directly
class CellHelper<T> {
    final T[] mValues;
    final Class<T> cls;
    // Helper needs Class<T> to work around type erasure
    public CellHelper(T[] values, Class<T> c) {
        mValues = values;
        cls = c;
    }
    public T getCell(int index) {
        if (index < mValues.length ) {
            return mValues[index];              
        }
        throw new IllegalArgumentException("Invalid " + cls.getSimpleName() + " value: " + index);
    }
}

enum SomeEnumClass {
    ONE(1), TWO(2), THREE(3);
    SomeEnumClass(int n){}
    // This variable hosts your static data, along with shared behavior
    private static  final CellHelper<SomeEnumClass> helper = new CellHelper(SomeEnumClass.values(), SomeEnumClass.class);
    // Delegate the calls for shared functionality to the helper object
    public static SomeEnumClass getCell(int i) {return helper.getCell(i);}
}

enum OtherEnumClass {
    Monday(1), Tuesday(2), Wednesday(3), Thrusday(4), Friday(5), Saturday(6), Sunday(7);
    OtherEnumClass(int n){}
    private static  final CellHelper<OtherEnumClass> helper = new CellHelper(OtherEnumClass.values(), OtherEnumClass.class);
    public static OtherEnumClass getCell(int i) {return helper.getCell(i);}
}

Demo. 演示。

Your code example is a bit misleading as it returns the constant with the same ordinal rather than the one with the same property value. 您的代码示例有点误导,因为它返回具有相同序数而不是具有相同属性值的常量。 In order to abstract the search for a constant with a property value, you have to abstract the property, eg 为了抽象具有属性值的常量的搜索,您必须抽象该属性,例如

interface TypeWithIntProperty {
  int getProperty();
}
enum Number implements TypeWithIntProperty {
  ONE(1), TWO(2), THREE(3);

  private final int value;

  Number(int value) {
    this.value=value;
  }
  public int getProperty() {
    return value;
  }
}
enum DayOfWeek implements TypeWithIntProperty {
  Monday(1), Tuesday(2), Wednesday(3), Thrusday(4), Friday(5), Saturday(6), Sunday(7);

  private final int value;

  DayOfWeek(int value) {
    this.value=value;
  }
  public int getProperty() {
    return value;
  }
}

public class Helper {
  public static <E extends Enum<E>&TypeWithIntProperty>
                E getEnumItem(Class<E> type, int value) {
    for(E constant: type.getEnumConstants())
      if(value == constant.getProperty())
        return constant;
    throw new IllegalArgumentException("no constant with "+value+" in "+type);
  }
}

DayOfWeek day=Helper.getEnumItem(DayOfWeek.class, 7);
Number no=Helper.getEnumItem(Number.class, 2);

If the properties have different types, you can make the interface generic: 如果属性具有不同的类型,则可以使接口通用:

interface TypeWithIntProperty<T> {
  T getProperty();
}
enum Number implements TypeWithIntProperty<String> {
  ONE, TWO, THREE;

  public String getProperty() {
    return name().toLowerCase();
  }
}
enum DayOfWeek implements TypeWithIntProperty<Integer> {
  Monday(1), Tuesday(2), Wednesday(3), Thrusday(4), Friday(5), Saturday(6), Sunday(7);

  private final int value;

  DayOfWeek(int value) {
    this.value=value;
  }
  public Integer getProperty() {
    return value;
  }
}

public class Helper {
  public static <E extends Enum<E>&TypeWithIntProperty<P>,P>
                  E getEnumItem(Class<E> type, P value) {
    for(E constant: type.getEnumConstants())
      if(value.equals(constant.getProperty()))
        return constant;
    throw new IllegalArgumentException("no constant with "+value+" in "+type);
  }
}

DayOfWeek day=Helper.getEnumItem(DayOfWeek.class, 7);
Number no=Helper.getEnumItem(Number.class, "two");

A cleaner, but more verbose (under Java 6) alternative is to separate the property abstraction from the type having the property: 一种更简洁但更冗长的选择(在Java 6下)是将属性抽象与具有属性的类型分开:

interface Property<T,V> {
  V get(T owner);
}
enum Number {
  ONE, TWO, THREE;
  static final Property<Number,String> NAME=new Property<Number,String>() {
    public String get(Number owner) { return owner.getName(); }
  };

  public String getName() {
    return name().toLowerCase();
  }
}
enum DayOfWeek {
  Monday(1), Tuesday(2), Wednesday(3), Thrusday(4), Friday(5), Saturday(6), Sunday(7);
  static final Property<DayOfWeek,Integer> INDEX=new Property<DayOfWeek,Integer>() {
    public Integer get(DayOfWeek owner) { return owner.getIndex(); }
  };

  private final int index;

  DayOfWeek(int value) {
    this.index=value;
  }
  public int getIndex() {
    return index;
  }
}
public class Helper {
  public static <E extends Enum<E>,P>
                  E getEnumItem(Class<E> type, Property<E,P> prop, P value) {
    for(E constant: type.getEnumConstants())
      if(value.equals(prop.get(constant)))
        return constant;
    throw new IllegalArgumentException("no constant with "+value+" in "+type);
  }
}

DayOfWeek day=Helper.getEnumItem(DayOfWeek.class, DayOfWeek.INDEX, 7);
Number no=Helper.getEnumItem(Number.class, Number.NAME, "two");

This would be much simpler in Java 8 where you can implement Property as DayOfWeek::getIndex or Number::getName instead of inner classes, on the other hand, since we don't benefit from the single-method interface in Java 6, we can turn this into an advantage by using an abstract base class which can provide the functionality, now even with caching: 另一方面,这在Java 8中要简单得多,在Java 8中,您可以将Property实现为DayOfWeek::getIndexNumber::getName而不是内部类,因为我们没有从Java 6的单方法接口中受益,因此,可以使用抽象的基类将其变成一个优势,该基类可以提供功能,即使现在使用缓存也可以:

abstract class Property<T extends Enum<T>,V> {
  final Class<T> type;
  final Map<V,T> map;
  Property(Class<T> type) {
    this.type=type;
    map=new HashMap<V, T>();
    for(T constant: type.getEnumConstants())
    {
      T old = map.put(get(constant), constant);
      if(old!=null)
        throw new IllegalStateException("values not unique: "+get(constant));
    }
  }
  abstract V get(T owner);
  T getConstant(V value) {
    T constant=map.get(value);
    if(constant==null)
      throw new IllegalArgumentException("no constant "+value+" in "+type);
    return constant;
  }
}
enum Number {
  ONE, TWO, THREE;
  static final Property<Number,String> NAME=new Property<Number,String>(Number.class) {
    public String get(Number owner) { return owner.getName(); }
  };

  public String getName() {
    return name().toLowerCase();
  }
}
enum DayOfWeek {
  Monday(1), Tuesday(2), Wednesday(3), Thrusday(4), Friday(5), Saturday(6), Sunday(7);
  static final Property<DayOfWeek,Integer> INDEX
               =new Property<DayOfWeek,Integer>(DayOfWeek.class) {
    public Integer get(DayOfWeek owner) { return owner.getIndex(); }
  };

  private final int index;

  DayOfWeek(int value) {
    this.index=value;
  }
  public int getIndex() {
    return index;
  }
}

DayOfWeek day=DayOfWeek.INDEX.getConstant(7);
Number no=Number.NAME.getConstant("two");

You can use an interface: 您可以使用一个接口:

public interface Indexed<E extends Enum> {

    default public E getByIndex(int index) {
        if (!this.getClass().isEnum()) {
            //not implemented on enum, you can do as you like here
        }
        Enum<?>[] vals = (Enum<?>[]) this.getClass().getEnumConstants();
        if (index < 0 || index >= vals.length) {
            //illegal arg exception
        }
        return (E) vals[index];
    }

}

Then in implementation: 然后在执行中:

public enum MyEnum implements Indexed<MyEnum> {
    ONE,
    TWO,
    THREE,
    ;
}

Also to note, instead of supplying those indexes manually, you can just use Enum#ordinal . 还要注意,您可以使用Enum#ordinal来代替手动提供这些索引。

This is a Java 8 only solution, since there are no defaulted methods in previous Java versions. 这是仅Java 8的解决方案,因为以前的Java版本中没有默认方法。 Additionally this has a bit of an awkward/disadvantageous usage, since it would be an instance method (though you could make it static if you desire). 另外,这有点笨拙/不利,因为它是实例方法(尽管您可以根据需要将其设置为静态)。

For earlier versions, you need a way to provide the class type since there isn't really a different way of providing it for an enum, in which you'd use David's answer above. 对于较早的版本,您需要一种提供类类型的方法,因为没有真正不同的方式为枚举提供类,您可以使用上面的David的答案。

The Helper class is really heplful. Helper类确实非常有用。 We already started having issue with bad factories, causing enums to be order dependant - which is a total aberation - 我们已经开始遇到不良工厂的问题,导致枚举依赖于订单-这是完全的错误-

Now I refactored all enums classes so they use the helper and a single factory. 现在,我重构了所有枚举类,以便它们使用帮助程序和单个工厂。 However I changed it's signature the following way: 但是我通过以下方式更改了它的签名:

public static <E extends Enum<E> & IEnumWithValue> E factory(final E[] iConstants, int iValue) throws IllegalArgumentException

In my enum class I have a member defined as this: 在我的枚举类中,我定义了一个成员:

private static  final MyEnum[]  mValues = MyEnum.values();

This way, I do not have to pass the enum type in argument, and I do not have to multiply calls to values() or class.getEnumConstants() 这样,我不必在参数中传递枚举类型,也不必倍增对values()class.getEnumConstants()的调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM