【问题标题】:How can I switch on the data type of an object field?如何打开对象字段的数据类型?
【发布时间】:2015-11-24 05:32:43
【问题描述】:

我有一个很长的 switch 语句,我正在努力提高效率。 它解析 xml 提要并使用 xml 值填充 Java 对象。 可能有 30 个字段,所以编写 switch case 有点乏味 每个字段。

switch (currentTagName) {
    case "longitude" :
        observation.setLongitude(Double.parseDouble(parser.getText()));
        break;
    case "elevation" :
        observation.setElevation(Integer.parseInt(parser.getText()));
        break;
    case "observation_time" :
        observation.setObservation_time(parser.getText());
        break;

您可以看到,每种情况的处理方式的唯一区别在于我正在处理的数据类型。

我正在尝试找出类似的语法(伪代码):

//get the data type of this variable, somehow or other
String inputType = Observation.getMethodInputType("set" + currentTagName); 

//switch on that data type
switch(inputType) {
    case "Integer":
        observation.set{currentTagName}(Integer.parseInt(parser.getText()));
        break;
    case "Double": 
        observation.set{currentTagName}(Double.parseDouble(parser.getText()));
        break;
    case "String":
        observation.set{currentTagName}(parser.getText());
        break;
}

只是我的 Java 语法很生疏,我不确定这样做的正确方法是什么(或者你是否可以), 特别是对于 set{currentTagName} 部分和 getMethodInputType() (这是一回事吗?)。

这样做的正确方法是什么?

【问题讨论】:

    标签: java


    【解决方案1】:

    将 int 和 double 转换为字符串,然后使用一个 if 语句。使用以下语法:

    Integer.toString(i)
    //Makes an integer into a string
    

    和

    Double.toString(i)
    
    //Makes a double into a string
    

    将所有内容都作为字符串后,比较字符串。这样变量类型是相同的,它使代码更容易处理。

    祝你好运!

    【讨论】:

      【解决方案2】:

      不要使用switch。创建一个接口,用于按类型将String 转换为对象,使用适当类型的转换器填充Map<Class,Converter>,并使用该映射中的转换器解析您的输入。

      interface Converter {
          Object convert(String s);
      }
      private static Map<Class,Converter> converterForClass = new HashMap<>();
      static {
          converterForClass.put(Integer.TYPE, s -> Integer.parseInt(s));
          converterForClass.put(Double.TYPE, s -> Double.parseDouble(s));
          converterForClass.put(String.class, s -> s);
          converterForClass.put(Long.TYPE, s -> Long.parseLong(s));
      };
      

      现在您可以将Strings 转换为适当类型的Objects,如下所示:

      Object a = converterForClass.get(Integer.TYPE).convert("123");
      

      这是一个使用反射访问字段的演示:

      static class Demo {
          public int a;
          public long b;
          public String c;
          public double d;
          @Override
          public String toString() {
              return a+":"+b+":'"+c+"':"+d;
          }
      }
      
      public static void main (String[] args) throws java.lang.Exception
      {
          Object a = new Demo();
          Map<String,String> data = new HashMap<>();
          data.put("a", "123");
          data.put("b", "123456678789898");
          data.put("c", "HELLO");
          data.put("d", "123.456");
          for (Field f : Demo.class.getDeclaredFields()) {
              f.setAccessible(true);
              String str = data.get(f.getName());
              Object r = converterForClass.get(f.getType()).convert(str);
              f.set(a, r); // Call set(target, objValue) instead of setInt, setLong, etc.
          }
          System.out.println(a);
      }
      

      Running demo.

      【讨论】:

      • 虽然是一个不错的解决方案,但为不同的标签调用不同的 setter 方法(具有不同的名称)的问题仍然存在。也许您可以通过一些仙尘(也称为反射)来改进您的解决方案,这会查找将数据放入的字段。
      • @ThomasJunk 必须为不同类型调用不同方法的问题完全消失了,因为有一个set 方法采用Object(参见演示)。当然,如果坚持根据类型调用不同的方法,而不是转换和返回对象,lambda 可以将字段和目标作为附加参数,并在内部进行设置。
      【解决方案3】:

      您可以简单地将其转换为对象并使用以下方法获取其类名:

      ((Object) yourVar).getClass().getName()
      

      然后你可以在你的 switch 语句中使用它:

      String typeName =  ((Object) inputType).getClass().getName();
      
      switch(typeName) { 
      
         case "Integer":      
      
                  observation.set{currentTagName}(Integer.parseInt(parser.getText())); 
         break; 
      
         case "Double":
      
                   observation.set{currentTagName}(Double.parseDouble(parser.getText())); 
          break; 
      
          case "String":
      
                    observation.set{currentTagName}(parser.getText()); 
          break;
       }
      

      查看How to determine the primitive type of a primitive variable? 了解更多信息。

      【讨论】:

      • 此解决方案可行,但这可能不是 switch 语句的好用例。
      • @ChakladerAsfakArefe 是的,我完全同意switch case 不是最好的解决方案,但我只是在纠正 OP 代码。
      【解决方案4】:

      您的解决方案将非常难以维护。 Java 几乎没有流行的以紧凑方式解析 xml 的方法(没有繁琐的字符串操作)。

      你应该检查:

      • JAXB(推荐),提供良好的域封装和非常可读的代码

      • DOM 半可读、半快速

      • SAX 速度非常快,维护性最低

      对于 JAXB,您必须创建对象/对象,这些对象将重新创建 xml 结构,并且会自动为您进行转换。

      【讨论】:

      • 哇,我忘记了 Java 在多大程度上拥有适用于所有内容的库。我认为 JAXB 是我所追求的。
      【解决方案5】:

      我编写了一个名为unXml 的小型Java-8 库,它可能对您有所帮助。它在 Github 上开源,在 Maven Central 上可用。

      您可以创建一个ObjectParser,它将从XPath 中挑选出一个值,并将其作为属性放入JsonObject。然后它使用 Jackson 的 json-mapping 来实例化你的类。

      阅读 Jacksons Json to Object mapping,了解如何最适合您的课程。

      例子:

      <root>
          <entry id="1">
              <observation>100</observation>
              <longitude>150</longitude>
              <observation-time>15 sec</observation-time>
          </entry>
      </root>
      

      假设您的专业领域是鸟类观察,这是您的课程:

      @JsonIgnoreProperties(ignoreUnknown = true)
      public static class BirdSpotting {
          public Integer id;
          public Integer observation;
          public Double longitude;
          public String observationTime;
      }
      

      您可以创建一个(可重复使用的)ObjectParser 和 apply 到您的 xml。

      public List<BirdSpotting> getData(Document inputXml) {
          Parsing parsing = ParsingFactory.getInstance().create();
      
          ObjectParser<List<BirdSpotting>> parser = 
              parsing.arr("/root/entry", parsing.obj()
                  .attribute("id", "@id")
                  .attribute("observation", "observation", parsing.with(Integer::parseInt))
                  .attribute("longitude", "longitude", parsing.with(Double::parseDouble))
                  .attribute("observationTime", "observation-time")
          ).as(BirdSpotting.class);
      
          List<BirdSpotting> spottings = parser.apply(inputXml);
          return spottings;
      }
      

      您还可以通过删除 parsing.arr()part 解析为单个 Birdspotting 对象。您的parser 将是ObjectParser&lt;Birdspotting&gt; 类型(没有List)。

      输出

      BirdSpotting [id=1,observation=100,longitude=150.0,observationTime="15 sec"]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-03-14
        • 1970-01-01
        • 2021-11-27
        • 1970-01-01
        • 1970-01-01
        • 2012-04-05
        • 1970-01-01
        • 2019-09-09
        相关资源
        最近更新 更多