【问题标题】:Best way to call different classes at runtime in Java在 Java 运行时调用不同类的最佳方法
【发布时间】:2019-03-03 04:26:05
【问题描述】:

假设我们必须在运行时根据定义从不同的类中调用一个方法。例如我们收到这样的 JSON:

{"calculator": "MyClass1", "parameter1": 1.0, "parameter2": 2.0, ... }

MyClass1 和更多类要么扩展一些基类,要么实现一些接口(只是为了能够在运行时枚举它们)。我们必须创建对象,将参数传递给对象并调用calculate()方法。

我可以想到两种方法来做到这一点:

  1. switch(calculatorString) { case "MyClass1":calculator = new MyClass1(); ...

  2. 使用 Java 反射

第一种方法真的很愚蠢,因为每次将新的计算器类添加到项目中时都必须更新代码。第二种方法稍好一些,但 IDE 无法捕获我们在创建对象和调用方法时犯的任何类型错误。

还有其他方法可以做到这一点(可能更好)?

【问题讨论】:

  • 可以使用工厂设计模式创建不同类的对象,然后调用calculate方法。这里工厂类将接受calculatorString,并基于此返回您调用的对象。然后使用该对象,您可以调用calculate 方法。

标签: java json reflection runtime


【解决方案1】:

您也许可以使用 Java 服务提供者接口:

请看这里的例子:

https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html
https://www.baeldung.com/java-spi

它允许您实现不同的实现/服务(您仍然需要服务文件中的类名),但不必直接使用反射。

您在META-INF中定义服务提供者

META-INF/services/com.baeldung.rate.spi.ExchangeRateProvider

您的服务可以自己注册,如果您允许它们使用输入 json,您将非常灵活。

【讨论】:

    【解决方案2】:

    反射是在运行时创建对象的最佳方式,但它完全取决于用例。 您还可以使用工厂设计模式来创建对象。

    使用反射API

    try {
                cls = Class.forName(className);
                instance = cls.newInstance();
                instance = BeanUtils.populateBean(properties, cls);
    } catch (Exception e) {
         e.printStackTrace();           
    }
    

    BeanUtil 类

    public static Object populateBean(Map<String, Object> propertyMap, Class<?> clazz) throws Exception {
        PropertyUtilsBean bean = new PropertyUtilsBean();
        Object obj = null;
        try {
            obj = clazz.newInstance();
    
            for(Map.Entry<String, Object> entrySet: propertyMap.entrySet()) {
                PropertyDescriptor descriptor = null;
                try {
                    descriptor =
                            bean.getPropertyDescriptor(obj, entrySet.getKey());
    
                    if (descriptor == null) {
                        continue;
                    }
                    Method writeMethod = bean.getWriteMethod(descriptor);
                    writeMethod.invoke(obj, convert(descriptor.getPropertyType(), entrySet.getValue(), DATE_PATTERN));
    
                } catch (IncompatibleConversion e) {
                    throw e;
                } catch (Exception e) {
                    throw new Exception("Unable to parse");
                }
            }
    
        }catch(Exception e) {
            throw e;
        }
    
        return obj;
    }
    

    转换类

    private static Object convert(Class<?> clzz, Object value, String datePattern) throws Exception {
    
        if (clzz.isAssignableFrom(BigInteger.class)) {
            if (value == null) {
                return value;
            }
    
            if (value instanceof BigInteger) {
                return value;
            }
    
            try {
                return new BigInteger(value.toString());
            } catch (Exception e) {
                throw new IncompatibleConversion(e);
            }
        } else if (clzz.isAssignableFrom(BigDecimal.class)) {
            if (value == null) {
                return value;
            }
    
            if (value instanceof BigDecimal) {
                return parseBigDecimal(value.toString(), DECIMAL_PRECISION);
            }
    
            try {
                return parseBigDecimal(value.toString(), DECIMAL_PRECISION);
            } catch (Exception e) {
                throw new IncompatibleConversion(e);
            }
        } else if (clzz.isAssignableFrom(Integer.class)) {
            if (value == null) {
                return value;
            }
    
            if (value instanceof Integer) {
                return value;
            }
    
            try {
                return new Integer(value.toString());
            } catch (Exception e) {
                throw new IncompatibleConversion(e);
            }
        } else if (clzz.isAssignableFrom(Long.class)) {
            if (value == null) {
                return value;
            }
    
            if (value instanceof Long) {
                return value;
            }
    
            try {
                return new Long(value.toString());
            } catch (Exception e) {
                throw new IncompatibleConversion(e);
            }
        } else if (clzz.isAssignableFrom(String.class)) {
            if (value == null) {
                return value;
            }
    
            if (value instanceof String) {
                return value;
            }
    
            try {
                return value.toString();
            } catch (Exception e) {
                throw new IncompatibleConversion(e);
            }
        } else if (clzz.isAssignableFrom(Date.class)) {
    
            if (value == null) {
                return value;
            }
    
            if (value instanceof Date) {
                return value;
            }
    
            if (datePattern == null) {
                throw new Exception("date pattern cannot be null");
            }
            try {
    
                SimpleDateFormat sdf = new SimpleDateFormat(datePattern);
                return sdf.parse(value.toString());
            } catch (Exception e) {
                throw new IncompatibleConversion(e);
            }
        } else if (clzz.isAssignableFrom(Byte.class)) {
            if (value == null) {
                return value;
            }
    
            if (value instanceof Byte) {
                return (value);
            } else if (value instanceof Number) {
                return new Byte(((Number) value).byteValue());
            }
    
            try {
                return (new Byte(value.toString()));
            } catch (Exception e) {
                throw new IncompatibleConversion(e);
            }
        } else if (clzz.isAssignableFrom(Float.class)) {
            if (value == null) {
                return value;
            }
    
            if (value instanceof Float) {
                return (value);
            }
    
            try {
                return new Float(value.toString());
            } catch (Exception e) {
                throw new IncompatibleConversion(e);
            }
        } else if (clzz.isAssignableFrom(Double.class)) {
            if (value == null) {
                return value;
            }
    
            if (value instanceof Double) {
                return (value);
            }
    
            try {
                return new Double(value.toString());
            } catch (Exception e) {
                throw new IncompatibleConversion(e);
            }
        }
    
        throw new Exception("Incompactible Conversion");
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-07
      • 2017-06-29
      • 1970-01-01
      • 2021-10-19
      • 1970-01-01
      • 2023-03-21
      • 1970-01-01
      • 2015-09-21
      相关资源
      最近更新 更多