【问题标题】:Java - Generics vs Casting ObjectsJava - 泛型与强制转换对象
【发布时间】:2012-07-09 19:25:31
【问题描述】:

我有一堂课Data<T>

具有通用属性

private T value;

有没有更好的方法来执行以下操作?
即以不同的形式返回泛型类型?

public List<String> getValues() {
    if (value.getClass() != ArrayList.class)
        throw new Exception("Wrong Enum value '%s'", value);
    return (ArrayList<String>) value;
    //ugly
}


public String getStringValue() {
    if (value.getClass() != String.class)
        throw new Exception("Wrong value type '%s'", value);
    return (String) value;
    //ugly
}

public Float getFloatValue() {
    if (value.getClass() != Double.class)
        throw new Exception("Wrong value type '%s'", value);
    return (Float) value;
    //ugly
}

public Long getLongValue() {
    if (value.getClass() != Double.class)
        throw new Exception("Wrong value type '%s'", value);
    return (Long) value;
    //ugly
}
public T getValue() {
    return value;
}

精确,我使用 Gson 作为反序列化器,来获取一个 List,然后每个 Data 对象都可以是异构的
也可以为浮点和长检测注册适配器,但它不会更快或更好

编辑:gson 无法检索 long:

要么:

 ((Long) d.getValue())

java.lang.Double 不能转换为 java.lang.Long

Long.parseLong( d.getValue().toString())

java.lang.NumberFormatException:对于输入字符串:“212231.0”

我尝试注册一个 LongAdpater

gsonBuilder.registerTypeAdapter(Long.class, new LongAdapter());

private static class LongAdapter implements 
    JsonSerializer<Long>, JsonDeserializer<Long> 
{

    @Override public Long deserialize(
            JsonElement json, 
            Type type,
            JsonDeserializationContext arg2) throws JsonParseException 
    {
        return json.getAsLong();
    }

    @Override
    public JsonElement serialize(Long l, Type arg1,
            JsonSerializationContext arg2) {
        return new JsonPrimitive(new Double(l));
    }
}

java.lang.IllegalArgumentException:无法为类 java.lang.Long 注册类型适配器

edit2 用于 tsOverflow:

Data<Float> d1 = new Data<Float>( new Float(6.32));
List<String> l = new ArrayList<String>();
    l.add("fr");
    l.add("it");
    l.add("en");
Data<List<String>> d2 = new Data<List<String>>( l);
Data<Long> d3 = new Data<Long>(new Long(212231));

List<Data> data = new ArrayList<Data>();
    data.add(d1);
    data.add(d2);
    data.add(d3)

new Gson().toJson(data);

【问题讨论】:

  • 您能否详细说明上述代码的动机?这似乎违背了使用泛型的目的。您能否提供: public T getValue() { return value;并让客户担心通过执行适当的转换来提取属性的“真”值?
  • 客户端将不得不担心字符串列表、字符串、浮点数或长字符串,这就是为什么最好将它们放在类中
  • 正如永恒00b 所说-需要更多关于您到底想要做什么的背景知识。您发布的代码 sn-p 似乎违背了泛型的目的。并且不确定您所说的“每个 Data 对象都可以是异构的”是什么意思 - 如果您正在接收 JSON 并将其通过 GSON 传递以进行反序列化 - 示例 JSON 会有所帮助。
  • 大多数情况下,正确的答案是退后一步,并确保您一开始就不会忘记值的类型。你从哪里得到这个?为什么要丢弃变量类型?
  • 别用异类的List,写一个三个字段的类。仅当所有元素都具有或多或少相同的含义时,您才应该使用Lists。

标签: java generics casting gson type-inference


【解决方案1】:

泛型的意义在于允许一个类同时使用不同的类型。

泛型允许您定义/限制对象的实例使用的类型。

泛型背后的想法是消除强制转换的需要。

在你的类中使用泛型应该会产生如下结果:

Data<String> stringData = new Data<String>();
String someString = stringData.getValue();

Data<Long> longData = new Data<Long>();
Long someLong = longData.getValue();

Data<List<String>> listData = new Data<List<String>>();
List<String> someList = listData.getValue();

您应该使用 Objects 和强制转换 --OR-- 使用泛型来避免强制转换。

您似乎相信泛型允许在同一个实例中进行异构类型。

这是不正确的。

如果您希望列表包含混合类型的包,那么泛型是不合适的。


还有……

要从双精度数创建一个长整数,请使用Double.longValue()

要从双精度数创建浮点数,请使用Double.floatValue()

我推荐阅读the documentation

【讨论】:

    【解决方案2】:

    这个设计对我来说看起来很可疑,但要回答你的实际问题:

    长值的情况看起来不对。您的 sn-p 包含 c&p 错误

    public Long getLongValue() {
        if (value.getClass() != Double.class) // <<- should be Long.class
            throw new Exception("Wrong value type '%s'", value);
        return (Long) value;
        //ugly
    }
    

    因此它应该是:

    public Long getLongValue() {
        if (value.getClass() != Long.class)
            throw new Exception("Wrong value type '%s'", value);
        return (Long) value;
        //ugly
    }
    

    但是,为了减少代码重复,您可以引入通用帮助方法

    private T getValue() {
        return value;
    }
    
    private <V> V castValue(Class<V> type) {
      if (!type.isInstance(value)) {
        // exception handling
      }
      return type.cast(value);
    }
    
    public List<String> getValues() {
        return castValue(ArrayList.class);
    }
    
    public String getStringValue() {
        return castValue(String.class);
    }
    

    如果您决定采用这种方法,我建议您对数据类进行反泛化,因为如果实例本身实际上没有约束,那么拥有类型参数会很烦人。我会使用 Object 代替字段类型:

    private Object getValue() {
        return value;
    }
    
    private <V> V castValue(Class<V> type) {
      if (!type.isInstance(value)) {
        // exception handling
      }
      return type.cast(value);
    }
    
    public List<String> getValues() {
        return castValue(ArrayList.class);
    }
    
    public String getStringValue() {
        return castValue(String.class);
    }
    // .. more cases ..
    

    【讨论】:

      【解决方案3】:

      您可以直接将类型 T 用于简单的 getter,将 Class.cast -method 用于其他类型:

      public class GenericDataTest
      {
          private static class DataTest<T>
          {
              private T value;
      
              public DataTest(T value)
              {
                  this.value = value;
              }
      
              public T getValue()
              {
                  return value;
              }
      
              public Object getValueAsType(Class<?> type)
              {
                  return type.cast(value);
              }
          }
      
          @Test
          public void testGeneric()
          {
              DataTest<String> stringTest = new DataTest<String>("Test");
              Assert.assertEquals("Test", stringTest.getValue());
              Assert.assertEquals("Test", stringTest.getValueAsType(String.class));
      
              DataTest<Double> doubleTest = new DataTest<Double>(1.0);
              Assert.assertEquals(1.0, doubleTest.getValue());
              Assert.assertEquals(1.0, doubleTest.getValueAsType(Double.class));
          }
      
          @Test(expected = ClassCastException.class)
          public void testClassCastFailure()
          {
              DataTest<String> stringTest = new DataTest<String>("Test");
              Assert.assertEquals("Test", stringTest.getValueAsType(Float.class));
          }
      }
      

      【讨论】:

        【解决方案4】:

        您可以询问“值”是否可分配给预期的类。

        private T value;
        .
        .
        .
        public Object getValueAsObjectOfClass(Class<?> expectedClass) {
            if(!expectedClass.isAssignableFrom(value.getClass())) {
                // abort gracefully
            }
            return expectedClass.cast(value);
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-12-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多