【问题标题】:Java way to create an object based on enum typeJava基于枚举类型创建对象的方法
【发布时间】:2015-06-18 19:31:22
【问题描述】:

我的课是这样的:

class X {}
class Y extends X {};
class Z extends X {};

每个子类(id + class)都有一个枚举:

enum Type {
   Y_TYPE(1, Y.class), Z_TYPE(2, Z.class);
   int id;
   Class c;
   public Type(int id, Class c) { this.id = id; this.c = c; }
   public static X createInstance() throws Exception {
      return c.newInstance();
   }
}

然后我使用它们如下:

X parseFromID(int id) {
   for (Type v : Type.values()) {
     if (v.id == id) {
        return v.createInstance();
     }
   }
}

它工作得很好,但我想知道这是否是一种基于整数 id 创建数据的 Java-ist 方式?有什么不好的地方需要寻找吗?

有没有办法强制传入的类是 X 类型而没有冗长的 if-else 条件?当我有大量子类时想想。


为什么要处理整数 id?

我正在编写某种解析器,因此我需要将从某处获取的整数 id 转换为适当的对象。

【问题讨论】:

    标签: java design-patterns enums dynamic-class-creation


    【解决方案1】:

    这里真的没有理由使用反射。抛出异常也是一种不好的做法,如果你不使用反射,你就不必处理反射异常。你可以简单地做

    enum Type {
        Y_TYPE(1) {
            @Override
            public X createInstance() {
                return new Y();
            }
        }, Z_TYPE(2) {
            @Override
            public X createInstance() {
                return new Z();
            }
        };
    
        private int id;
    
        private Type(int id) { 
            this.id = id; 
        }
    
        public abstract X createInstance();
    }
    

    这也很有帮助,因为它不会强制每个子类都有一个公共的无参数构造函数,并且如果可能的话,还允许返回相同的 X 或 Y 实例。

    如果您担心匿名类定义的冗长,如果您使用的是 Java 8,则可以将它们替换为 lambda:

    import java.util.function.Supplier;
    
    enum Type {
        Y_TYPE(1, X::new), Z_TYPE(2, Y::new);
    
        private int id;
        private Supplier<X> supplier;
    
        private Type(int id, Supplier<X> supplier) {
            this.id = id;
            this.supplier = supplier;
        }
    
        public X createInstance() {
            return supplier.get();
        }
    }
    

    【讨论】:

    • 我考虑过这个,但是如果我有大量的子类,它很快就会变成很长的代码吧?
    • 有点冗长,但没那么长。请参阅我编辑的答案以获得不那么冗长的解决方案。
    • 第二个版本看起来和我的差别不大,主要优点是什么?
    • 优势就在答案中。阅读。没有例外、类型安全、实施供应商的灵活性、更好的性能
    • 我们如何才能满足通过参数化构造器创建对象的需求?
    【解决方案2】:

    使用工厂和地图更具学术性:

    import java.util.HashMap;
    import java.util.Map;
    
    interface Factory<T> {
    
        T createInstance();
    }
    
    class X {/**/}
    class Y extends X {/**/}
    class Z extends X {/**/}
    
    class Factories {
    
       static Map<Integer, Factory<?>> factories = new HashMap<>();
       static {
          factories.put( 1, X::new );
          factories.put( 2, Y::new );
          factories.put( 3, Z::new );
       }
    
       @SuppressWarnings("unchecked")
       static <T> Factory<T> get( int id ) {
          return (Factory<T>)factories.get( id );
       }
    }
    
    public class Main {
    
       static void main( String[] args ) {
          final Factory<X> fx = Factories.get( 1 );
          final X x = fx.createInstance();
          final Factory<Y> fy = Factories.get( 2 );
          final Y y = fy.createInstance();
          final Factory<Z> fz = Factories.get( 3 );
          final Z z = fz.createInstance();
       }
    }
    

    【讨论】:

      【解决方案3】:

      有没有办法强制传入的类是 X 类型而没有冗长的 if-else 条件?

      是的,您可以使用泛型来限制类。将构造函数更改为:

      public Type(int id, Class<? extends X> c) { this.id = id; this.c = c; }
      

      为什么要处理整数 ID?您可以直接使用枚举值,或者 - 如果需要传输或存储它们 - 它们的字符串表示形式,并在需要时使用枚举的 valueOf 方法解析字符串。

      【讨论】:

        猜你喜欢
        • 2019-04-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多