【问题标题】:Using generics for a class how can I convert the values from one type to another?对类使用泛型如何将值从一种类型转换为另一种类型?
【发布时间】:2017-01-04 03:42:17
【问题描述】:

我构建了一个简单的文档存储,其中的实体具有不同类型的字段,我有 Float、Int 和 String 类型。实体包含值的数组列表,如果有人更新实体的架构,我希望能够尝试将值转换为新类型。

public interface FieldType<T> {
    ArrayList<T> values;
}

public class FloatField implements FieldType<Float> {

}

public class StringField implements FieldType<String> {

}

我曾考虑过使用具有以下方法的抽象类

public abstract class Field<T> implements FieldType<T> {
  abstract public <T> castFromString(String value);
  abstract public <T> castFromFloat(Float value);
  abstract public <T> castFromInt(Int value);
}

public class FloatField extends Field<Float> {
  @override
  public <Float> castFromString(String value){
    Float castValue = null;
    try {
      castValue = Float.parseFloat(value);
    } catch(Exception e){

    }

    return castValue;
  }
}

我不太喜欢这种解决方案,因为每次向系统添加额外类型时,我都必须添加一个新的抽象方法。

有什么想法可以更好地实现吗?

【问题讨论】:

    标签: java generics


    【解决方案1】:

    也许您可以使用Function&lt;T, R&gt; interface

    public abstract class Field<T> implements FieldType<T> {
    
        ...
    
        public <F> T convert(F value, Function<F, T> converter) {
            try {
                return converter.apply(value);
            } catch(Exception e) {
                return null;
            }
        }
    
        ...
    
    }
    

    然后使用lambda expressionmethod reference 指定转换器:

    field.convert("1234", BigDecimal::new); //with a method reference
    field.convert("1234", s -> new BigDecimal(s)) //with a lambda
    

    这会将您的所有convertXXX 方法替换为一个,因为返回类型是从传递的Function 推断出来的。


    编辑: 如果您想要自动转换,您当然必须对这些进行硬编码,因为您不想在 Java API 中为所有 4240 个类编写转换方法。不过,这会变得混乱。可能在静态帮助程序类或FieldType 本身中有类似的东西?

    public class WhereverYouWantThis {
    
        private static HashMap<Class<?>, HashMap<Class<?>, Function<?, ?>>> converters = new HashMap<>();
    
        static {
            putConverter(String.class, Float.class, Float::parseFloat);
        }
    
        private static <T, R> void putConverter(Class<T> t, Class<R> r, Function<T, R> func) {
            HashMap<Class<?>, Function<?, ?>> map = converters.get(t);
            if(map == null) converters.put(t, map = new HashMap<>());
            map.put(r, func);
        }
    
        public static <T, R> Function<T, R> getConverter(Class<T> t, Class<R> r) {
            HashMap<Class<?>, Function<?, ?>> map = converters.get(t);
            if(map == null) return null;
            @SuppressWarnings("unchecked")
            Function<T, R> func = (Function<T, R>) map.get(r);
            return func;
        }
    
        public static <T, R> R convert(T o, Class<R> to) {
            @SuppressWarnings("unchecked")
            Function<T, R> func = (Function<T, R>) getConverter(o.getClass(), to);
            return func == null ? null : func.apply(o);
        }
    
    }
    

    【讨论】:

    • 这看起来不错,如果不能将return语句转换为正确的类型,我是否必须将return语句包装在try catch中?
    • 如果你的意思是格式无效,是的(例如convert("a", Float::parseFloat))。修复了
    • 有没有办法让FloatField返回转换器,或者有可以调用convert的方法,这样我就不用想办法传什么函数了?
    • @CharlesBryant 添加了一个示例。您将收到类型警告,但您可以 @SuppressWarnings 它们因为您可以证明它是类型安全的 - addConverter 方法只能将 Functions 添加到地图中,前提是它们具有正确的类型反正(就是不要直接添加到地图上,这是个坏主意)
    【解决方案2】:

    我认为您不需要为此使用泛型。相反,只需尝试从输入 String 创建一个 Float 并在出现问题时返回 null

    public Float castFromString(String value) {
        Float castValue = null;
        try {
          castValue = Float.parseFloat(value);
        } catch(Exception e){
            // log here
        }
    
        return castValue;
    }
    

    我认为不需要泛型的原因是转换中涉及的类型在您的辅助方法中已命名/已知。

    【讨论】:

      猜你喜欢
      • 2012-03-28
      • 1970-01-01
      • 1970-01-01
      • 2010-09-23
      • 2011-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多