【问题标题】:Java dynamic casting for overloaded methods重载方法的Java动态转换
【发布时间】:2009-08-15 15:22:34
【问题描述】:

我正在尝试创建一个函数,我可以在其中传递将被放入ContentValues 的名称和值。我遇到的问题是我试图允许一次传递多个键/值对,因此键可以是字符串数组,但值必须是对象,而 ContentValues.put() 不是允许对象,它们需要被强制转换为字符串、双精度、浮点数等。有没有办法可以确定对象的类型并将其强制转换,以便调用正确的 put()?下面是一种可行的方法,但它需要我为每种可能的值类型添加一个 if。

 public long create(String[] names, Object[] values) {
        ContentValues initialValues = new ContentValues();

        for (int i = 0; i < names.length; i++)
        {
            String type = values[i].getClass().getName();
            if (type.equals("java.lang.Double"))
            {
                initialValues.put(names[i], (Double)values[i]);
            }
            else if (type.equals("java.lang.String")) {
                initialValues.put(names[i], (String)values[i]);
            }
            else
            {
                throw new InvalidParameterException("Unable to convert type:"+type);
            }
        }

        return mDb.insert(this.getTableName(), null, initialValues);
    }

【问题讨论】:

  • 我正在为 android 编写,据我所知它使用 Java5,但我是 Java 新手,我不知道如何验证。

标签: java


【解决方案1】:

您的方法是正确的,但您应该使用instanceof 而不是getClass().getName()。即:

if (values[i] == null) {
  initialValues.putNull(names[i]);
} else if (values[i] instanceof Boolean) {
  initialValues.put(names[i], (Boolean)values[i]);
} else if (values[i] instanceof Byte) {
  initialValues.put(names[i], (Byte)values[i]);
} else if (values[i] instanceof Double) {
  initialValues.put(names[i], (Double)values[i]);
} else if (values[i] instanceof Float) {
  initialValues.put(names[i], (Float)values[i]);
} else if (values[i] instanceof Integer) {
  initialValues.put(names[i], (Integer)values[i]);
} else if (values[i] instanceof Long) {
  initialValues.put(names[i], (Long)values[i]);
} else if (values[i] instanceof Short) {
  initialValues.put(names[i], (Short)values[i]);
} else if (values[i] instanceof String) {
  initialValues.put(names[i], (String)values[i]);
} else if (values[i] instanceof byte[]) {
  initialValues.put(names[i], (byte[])values[i]);
} else if (values[i] instanceof ContentValues) {
  initialValues.putAll(names[i], (ContentValues)values[i]);
} else {
  throw new IllegalArgumentException(
      "can't put " + values[i].getClass().getName() + " in ContentValues.");
}

另一种选择是使用反射,但我认为反射是最后的手段。

如果ContentValues 不是final,另一种选择是扩展它并添加一个采用Object 的方法。

【讨论】:

  • 为什么您认为反思是最后的手段?对我来说这似乎是一个好方法。
  • 为什么要使用instanceof?原语的包装器总是最终的.. 不需要检查子类
  • dfa: instanceof 更通用,更简洁。为什么你使用instanceof?
  • @Asa:除了昂贵之外,反射通常会导致代码更难维护并且会破坏静态分析(包括编译时类型检查、重构工具等)。一般来说,如果你不使用反射也能完成同样的事情,那就不要使用反射。
【解决方案2】:

你有两个选择:

  1. 像您正在做的那样创建一个 if-else 块列表。如果你这样做,我不会那样做。我会改为“ob instanceof String”;或
  2. 使用反射为传入的类型找到正确的方法。

要考虑的一件事是空值。如果传入的值为 null 则没有类,因此您无法确定类型,如果这与您的用法相关。

【讨论】:

  • 为什么要使用instanceof?原始的包装器总是最终的.. 不需要检查子类
  • 在这种情况下,instanceof 应该比 String.equals() 调用更快。一般来说,我们不能假设所有类型都是最终的。事实上,如果涉及到类加载器,比较类名可能不足以区分类。
  • 您也可以为基元执行 (object.getClass() == String.class) 而不是 instanceof。这可能是暗示的,而不是 .equals()。
【解决方案3】:

IMO,这与你能做到的一样简洁、高效和正确。编译器可能会优化类型转换,但这并不重要。

public long create(String[] names, Object[] values) {
    ContentValues initialValues = new ContentValues();
    for (int i = 0; i < names.length; i++) {
        Object v = values[i];
        if (v instanceof Double) {
            initialValues.put(names[i], (Double) v);
        } else if (v instanceof String) {
            initialValues.put(names[i], (String) v);
        } else {
            // You could test for v == null here ... otherwise you'll
            // get an NPE 
            throw new InvalidParameterException(
                    "Unable to convert type: " + v.getClass());
        }
    }
    return mDb.insert(this.getTableName(), null, initialValues);
}

一些注意事项:

  1. 编辑:一般来说,您可能不想测试确切的 C 类。您更有可能想要测试“C 或 C 的子类”。您不能使用类名对此进行测试。为此请使用 instanceof
  2. 如果您测试类名,请确保使用正确的类名。例如Double.class.getName() 实际上给出的是"java/lang/Double" 而不是"java.lang.Double"
  3. 除了比instanceof 慢之外,比较类名并不总是能给出正确的答案。如果某些东西正在使用类加载器技巧,则不同的类可以具有与getName()getCanonicalName() 报告的相同的名称。
  4. 避免对此类事情使用反射。它不可避免地会变慢,并且编译器将无法识别您的很多错误。

【讨论】:

    【解决方案4】:

    可以说,ContentValues 应该按照您将要编写的内容提供了一个 putObject() 方法。我可能倾向于使用提供该方法的类来扩展或以其他方式包装 ContentValue,从而使代码可重用。

    您使用的 if/else 方法可能会让人觉得平淡无奇,但会明确说明您的意思,并且生成简单、可维护的代码是我的首要任务。使用反射也是可能的,但实现起来更复杂。我会坚持你正在做的事情。

    【讨论】:

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