Java Collections包装器的解决方法打破了反射

时间:2009-09-17 01:02:48

标签: java reflection

今天,我发现使用Collections.synchronizedXXX并不适合反射。

这是一个简单的例子:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Weird{
  public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    list.add("Hello World");

    List<String> wrappedList = Collections.synchronizedList(list);

    printSizeUsingReflection(list);
    printSizeUsingReflection(wrappedList);
  }

  private static void printSizeUsingReflection(List<String> list) {
    try {
      System.out.println(
          "size = " + list.getClass().getMethod("size").invoke(list));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

第一次调用printSizeUsingReflection打印大小(即“1”),第二次调用结果为:

java.lang.IllegalAccessException: Class Weird can not access a member of class
    java.util.Collections$SynchronizedCollection with modifiers "public"
  at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
  at java.lang.reflect.Method.invoke(Method.java:588)
  at Weird.printSizeUsingReflection(Weird.java:18)
  at Weird.main(Weird.java:13)

这有点令人惊讶和恼人。有一个很好的解决方法吗?我知道java.util.concurrent中有一个线程安全的List实现,但该实现似乎比使用Collections.synchronizedList()慢。

4 个答案:

答案 0 :(得分:5)

抛出异常的原因是通过调用获得的类

list.getClass()

System.out.println("size = " + list.getClass().getMethod("size").invoke(list));

返回实际类型 - java.util.Collections $ SynchronizedCollection。 SynchronizedCollection类恰好是Collections类的内部类,您无法访问它,因此异常。

绕过这个异常的最好方法是在更合适的类/接口上调用size方法 - 通常这恰好是实现类(因为它肯定包含方法声明或定义),但在我们的例子中我们需要在公共类型上调用size(),因为obj.getClass()返回了非公共类型。具体来说,我们需要在java.util.Collection(超级)接口或java.util.List接口上调用size()。

以下语句将在控制台中打印出“size = 1”:

System.out.println("size = " + Collection.class.getMethod("size").invoke(list));
System.out.println("size = " + List.class.getMethod("size").invoke(list));

<强>更新

如果您想知道通过反射调用的size()方法是否同步,答案是肯定的,它是同步的。尽管在超类型 - Collection / List上调用了size()方法,但同步调用了SynchronizedCollection内部类中的size()方法,这恰好是同步的。这是一个实现细节,但保证在收集包装后起作用。

此外,在the supertype - the Collection interface contains the size() method时使用反射不是一个好主意。

答案 1 :(得分:3)

Collection.class获取方法(更常见的是迭代超类(和接口)以找到公共的东西)。或者只是不要使用反射。

答案 2 :(得分:1)

java.util.Collections $ SynchronizedCollection 是非公开类。

答案 3 :(得分:0)

确定 java.util.concurrent选择会慢一点(或者说,慢得多担心?)。

我猜你的两个选择是:

  1. 的LinkedBlockingQueue
  2. 的CopyOnWriteArrayList
  3. 我认为对于某些用例,这些实现实际上可能更快。另外,如果您最终需要它,java.util.concurrent中的集合可以处理并发修改。

    来自“Java Concurrency In Practice”的Concurrent Collections部分:

      

    CopyOnWriteArrayList是并发的   替换同步列表   在某些方面提供更好的并发性   常见情况并消除了   需要锁定或复制集合   在迭代期间。 (类似地,   CopyOnWriteArraySet是一个并发   替换同步集。)