简体   繁体   English

如何使用静态成员泛化Java枚举?

[英]How to genericize a Java enum with static members?

I am refactoring a part of our legacy app which handles exporting and importing of DB tables from/to Excel sheets. 我正在重构遗留应用程序的一部分,该应用程序处理从/到Excel工作表的数据库表的导出和导入。 We have a Formatter subclass for each table, to provide the definition of that table: how many columns it has, and what is the name, format and validator of each column. 我们为每个表都有一个Formatter子类,以提供该表的定义:它具有多少列,以及每列的名称,格式和验证器是什么。 The getters which supply this data are then called by a Template Method which exports/imports the table. 然后,通过导出/导入表的模板方法调用提供此数据的吸气剂。 I have extracted the column data into an enum, which greatly simplified the code. 我已经将列数据提取到一个枚举中,这大大简化了代码。 A formatter now looks like this (some details omitted for brevity): 格式化程序现在看起来像这样(为简洁起见,省略了一些详细信息):

public class DamageChargeFormatter extends BaseFormatter {
    public static final int NUM_COLUMNS = 7;

    public enum Column {
        VEHICLE_GROUP(0, "Vehicle Group", /* more params */),
        NAME_OF_PART(1, "Name of Part", /* more params */),
        //...
        LOSS_OF_USE(6, "Loss of Use", /* more params */);

        private static final Map<Integer, Column> intToColumn = new HashMap<Integer, Column>();

        static {
            for (Column type : values()) {
                intToColumn.put(type.getIndex(), type);
            }
        }

        public static TableColumn valueOf(int index) {
            return intToColumn.get(index);
        }

        private int index;
        private String name;

        Column(int index, String name, /* more params */) {
            this.index = index;
            this.name = name;
            //...
        }

        public int getIndex() { return index; }

        public String getName() { return name; }

        // more members and getters...
    }

    protected String getSheetName() {
        return "Damage Charges";
    }

    public String getColumnName(int columnNumber) {
        TableColumn column = Column.valueOf(columnNumber);

        if (column != null) {
            return column.getName();
        }
        return null;
    }

    // more getters...

    protected int getNumColumns() {
        return NUM_COLUMNS;
    }

    protected boolean isVariableColumnCount() {
        return false;
    }
}

Now, I have about a dozen such classes, each of which containing exactly the same code except that NUM_COLUMNS and the enum values of Column are different. 现在,我大约有十二个这样的类,除了NUM_COLUMNSColumn的枚举值不同之外,每个类都包含完全相同的代码。 Is there any way to genericize this somehow? 有什么办法可以某种方式将其泛化吗? The main obstacle to this is the static Column.valueOf() method and the static constant NUM_COLUMNS . 这样做的主要障碍是静态Column.valueOf()方法和静态常量NUM_COLUMNS Another concern with latter is that it really belongs to an abstraction one level higher, ie to the table, not to an individual column - it would be nice to somehow incorporate this into the generic solution. 后者的另一个担心是,它确实属于更高一级的抽象,即属于表,而不是单个列-最好以某种方式将其合并到通用解决方案中。

Technically I could solve this with a base interface ( TableColumn below) and reflection, but I don't really like that, as apart from trading compile time errors to runtime errors, it makes the code ugly (to me): 从技术上讲,我可以使用基本接口(下面的TableColumn )和反射来解决此问题,但是我不是很喜欢,除了将编译时错误换成运行时错误外,这对我来说代码也很丑陋:

public class GenericFormatter<E extends TableColumn> extends BaseFormatter {
    private Method valueOfMethod;

    public GenericFormatter(Class<E> columnClass) {
        try {
            valueOfMethod = columnClass.getDeclaredMethod("valueOf", Integer.class);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    public String getColumnName(int columnNumber) {
        try {
            @SuppressWarnings("unchecked")
            E elem = (E) valueOfMethod.invoke(columnNumber);

            if (elem != null) {
                return elem.getName();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    //...
}

Note that this code is purely experimental, as yet untested... 请注意,此代码纯粹是实验性的,尚未经过测试...

Is there a nicer, cleaner, safer way? 有没有更好,更清洁,更安全的方法?

May be, something like this: 可能是这样的:

public class TableMetadata<E extends Enum & TableColumn> {
    private Map<Integer, TableColumn> columns = new HashMap<Integer, TableColumn>();

    public TableMetadata(Class<E> c) {
        for (E e: c.getEnumConstants()) {
            columns.put(e.getIndex(), e);
        }
    }

    public String getColumnName(int index) {
        return columns.get(index).getName();
    }
}

public class GenericFormatter<E extends TableColumn> extends BaseFormatter {  
    private TableMetadata<E> m;  

    public GenericFormatter(TableMetadata<E> m) {  
         this.m = m;
    }  

    public String getColumnName(int columnNumber) {  
        return m.getColumnName(index);
    }  

    //...  
}

EDIT: Enum added to the type parameter for more compile-time safety 编辑:将 Enum添加到type参数以获得更多的编译时安全性

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

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