Java枚举:实现通用方法,避免重复
Java enums: implementing common methods, avoiding duplication
假设我有两个不同的枚举
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)
}
枚举在它们携带的数据类型(此处为 int)上具有共同点,并且在它们的名称和可能值的数量上有所不同。
对于这些枚举中的每一个,我都有几种实现方法,它们完全相同。示例:
public static OtherEnumClass getCell(int index)
{
if (index < OtherEnumClass .mValues.length )
{
return OtherEnumClass .mValues[index];
}
throw new IllegalArgumentException("Invalid " + OtherEnumClass .class.getSimpleName() + " value: " + index);
}
我正试图找到一种方法来避免重复这些方法,就像我对抽象 类 所做的那样。但到目前为止,我一无所有。
我们正在使用 java 1.6,暂时无法升级。任何帮助表示赞赏。谢谢。
你可以这样做:
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));
}
它打印:
ONE
Thrusday
THREE
Sunday
已编辑:
这与@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));
}
您可以将您的实现包装到通用帮助器 class 中,并在您的所有实现中使用它。不幸的是,您必须将调用复制到助手中; 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);}
}
您可以使用接口:
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];
}
}
然后在执行中:
public enum MyEnum implements Indexed<MyEnum> {
ONE,
TWO,
THREE,
;
}
另请注意,您可以只使用 Enum#ordinal
.
而不是手动提供这些索引
这是一个仅 Java 8 的解决方案,因为在以前的 Java 版本中没有默认方法。此外,这有一些 awkward/disadvantageous 用法,因为它是一个实例方法(尽管如果需要,您可以将其设为静态)。
对于早期版本,您需要一种方法来提供 class 类型,因为实际上并没有不同的方法为枚举提供它,您可以在其中使用上面 David 的回答。
您的代码示例有点误导,因为它 returns 具有相同序数的常量而不是具有相同 属性 值的常量。为了抽象搜索具有 属性 值的常量,您必须抽象 属性,例如
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);
如果属性有不同的类型,您可以使接口通用:
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");
更简洁但更冗长(在 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");
这在 Java 8 中会简单得多,您可以将 Property
实现为 DayOfWeek::getIndex
或 Number::getName
而不是内部 classes,在另一方面,由于我们没有从 Java 6 中的单一方法接口中获益,我们可以通过使用可以提供功能的抽象基 class 将其转化为优势,现在甚至可以使用缓存:
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");
帮手class真的很有帮助。我们已经开始遇到坏工厂的问题,导致枚举依赖于顺序——这是一个完全的偏差——
现在我重构了所有枚举 classes 以便它们使用助手和单个工厂。但是我通过以下方式更改了它的签名:
public static <E extends Enum<E> & IEnumWithValue> E factory(final E[] iConstants, int iValue) throws IllegalArgumentException
在我的枚举中 class 我有一个定义如下的成员:
private static final MyEnum[] mValues = MyEnum.values();
这样,我就不必在参数中传递枚举类型,也不必多次调用 values() 或 class.getEnumConstants()
假设我有两个不同的枚举
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)
}
枚举在它们携带的数据类型(此处为 int)上具有共同点,并且在它们的名称和可能值的数量上有所不同。
对于这些枚举中的每一个,我都有几种实现方法,它们完全相同。示例:
public static OtherEnumClass getCell(int index)
{
if (index < OtherEnumClass .mValues.length )
{
return OtherEnumClass .mValues[index];
}
throw new IllegalArgumentException("Invalid " + OtherEnumClass .class.getSimpleName() + " value: " + index);
}
我正试图找到一种方法来避免重复这些方法,就像我对抽象 类 所做的那样。但到目前为止,我一无所有。
我们正在使用 java 1.6,暂时无法升级。任何帮助表示赞赏。谢谢。
你可以这样做:
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));
}
它打印:
ONE
Thrusday
THREE
Sunday
已编辑: 这与@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));
}
您可以将您的实现包装到通用帮助器 class 中,并在您的所有实现中使用它。不幸的是,您必须将调用复制到助手中; 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);}
}
您可以使用接口:
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];
}
}
然后在执行中:
public enum MyEnum implements Indexed<MyEnum> {
ONE,
TWO,
THREE,
;
}
另请注意,您可以只使用 Enum#ordinal
.
这是一个仅 Java 8 的解决方案,因为在以前的 Java 版本中没有默认方法。此外,这有一些 awkward/disadvantageous 用法,因为它是一个实例方法(尽管如果需要,您可以将其设为静态)。
对于早期版本,您需要一种方法来提供 class 类型,因为实际上并没有不同的方法为枚举提供它,您可以在其中使用上面 David 的回答。
您的代码示例有点误导,因为它 returns 具有相同序数的常量而不是具有相同 属性 值的常量。为了抽象搜索具有 属性 值的常量,您必须抽象 属性,例如
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);
如果属性有不同的类型,您可以使接口通用:
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");
更简洁但更冗长(在 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");
这在 Java 8 中会简单得多,您可以将 Property
实现为 DayOfWeek::getIndex
或 Number::getName
而不是内部 classes,在另一方面,由于我们没有从 Java 6 中的单一方法接口中获益,我们可以通过使用可以提供功能的抽象基 class 将其转化为优势,现在甚至可以使用缓存:
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");
帮手class真的很有帮助。我们已经开始遇到坏工厂的问题,导致枚举依赖于顺序——这是一个完全的偏差——
现在我重构了所有枚举 classes 以便它们使用助手和单个工厂。但是我通过以下方式更改了它的签名:
public static <E extends Enum<E> & IEnumWithValue> E factory(final E[] iConstants, int iValue) throws IllegalArgumentException
在我的枚举中 class 我有一个定义如下的成员:
private static final MyEnum[] mValues = MyEnum.values();
这样,我就不必在参数中传递枚举类型,也不必多次调用 values() 或 class.getEnumConstants()