我被告知不要在这里使用反射...为什么不呢?

时间:2012-01-16 19:24:03

标签: java reflection

此代码用于对List进行排序。该列表可以包含数千个但不到10k的元素。

protected <E> int compareFields(E o1, E o2, String fieldName){
    try { 
        Comparable o1Data = (Comparable) o1.getClass().getMethod(fieldName).invoke(o1);
        Comparable o2Data = (Comparable) o2.getClass().getMethod(fieldName).invoke(o2);
        return o1Data == null ? o2Data == null ? 0 : 1 :
               o2Data == null ? -1 : o1Data.compareTo(o2Data);
    } catch(Exception e) {
        throw new RuntimeException(e);
    }
}

我被建议

  

“请不要对这样的事情使用反射!!   要么使用合适的比较器提供方法,要么提取相关属性的方法(可能以原始类型不支持的方式计算),或两者兼而有之。“

更好的方法就是一个例子。

上下文: 我有很多带数据表的屏幕。每个都是从List构建的。每个数据表需要按其6列中的每一列进行排序。列是Date或String。

3 个答案:

答案 0 :(得分:5)

在这里使用反射可能会慢得多,因为您使用getClassgetMethodinvoke为每次比较添加了多个堆栈帧,而不是使用对象的本机比较方法

理想情况下,您可以编写方法以避免在签名中使用object。 “合适的比较器”至少会强烈绑定到对象的类型(您假设它们是相同的)。如果必须进行动态字段比较(如图所示),那么至少可以将反射封装在该比较器中。

但是,如果要打几次这样的话,最好将比较器预先绑定到要排序的字段。这样,您只需预先调用getMethod,而不是每次比较一次。

答案 1 :(得分:1)

如果没有上下文,很难给出一个很好的例子,所以现在这里列出了为什么它不是最好的想法的小清单:

  1. 提供的字段无法保证可比较(不确定为什么此处的代码需要捕获该异常并重新命名)。
  2. 如果提供的对象类型不是以这种方式进行比较怎么办? (这是一个过于通用的方法名称,知道它应该如何使用)。
  3. 它不是强类型的。将字段名称作为字符串提供意味着您必须在属性名称更改时随处更改代码,并且很难找到您需要进行更改的位置。
  4. 反射可能比以强类型方式实现的速度慢。

答案 2 :(得分:1)

其他答案描述了为什么不建议使用反射。我想使用更传统的解决方案添加一个示例。

您应该将Comparator实例作为参数,而不是指定用于比较两个对象的字段。这样,使用此方法的客户端可以指定如何比较两个对象。

protected <E> int compareFields(E o1, E o2, Comparator<E> comparator) {
    return comparator.compare(o1, o2);
}

对此函数的示例调用如下所示:

MyClass a = ...;
MyClass b = ...;
Comparator<MyClass> intFieldComparator = new Comparator<MyClass> {
    public int compare(MyClass o1, MyClass o2) {
        int field1 = o1.getIntField();
        int field2 = o2.getIntField();

        return field2 - field1;
    }
};

compareFields(a, b, intFieldComparator);

如果要使用多个字段比较对象,可以定义不同的比较器。