Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Workaround for Java Collections wrappers breaking reflection

Today, I discovered that using Collections.synchronizedXXX doesn't play well with reflection.

Here's a simple example:

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();
    }
  }
}

The first call to printSizeUsingReflection prints the size (i.e. "1"), the second call results in:

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)

This is a little surprising and annoying. Is there a good workaround? I know there is a thread-safe List implementation in java.util.concurrent, but that implementation seems slower than using Collections.synchronizedList().

like image 881
Leo P Avatar asked Sep 17 '09 01:09

Leo P


3 Answers

The reason for the exception being thrown is that the class obtained by the invocation

list.getClass()

in the line

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

returns the actual type - java.util.Collections$SynchronizedCollection. The SynchronizedCollection class happens to be an inner class of the Collections class, which you cannot access, hence the exception.

The best way to bypass this exception is to invoke the size method on a more suitable class/interface - usually this happens to be the implementation class (since that would definitely contain the method declaration or definition), but in our case we need to invoke size() on a public type given that obj.getClass() has returned a non-public type. To be specific, we need to invoke size() on the java.util.Collection (super)interface or the java.util.List interface.

The following statements will print out "size = 1" in the console:

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

Update

In case you were wondering whether the size() method invoked via reflection is synchronized or not, the answer is yes, it is synchronized. Despite invoking the size() method on the super type - Collection/List, the size() method in the SynchronizedCollection inner class gets invoked, and this happens to be synchronized. This is an implementation detail though, guaranteed to work since the collection is wrapped.

Besides, it it not a good idea to use reflection when the supertype - the Collection interface contains the size() method.

like image 144
Vineet Reynolds Avatar answered Oct 22 '22 23:10

Vineet Reynolds


Get the method from Collection.class (more generally iterate up super classes (and interfaces) to find something public). Or just don't use reflection.

like image 26
Tom Hawtin - tackline Avatar answered Oct 23 '22 00:10

Tom Hawtin - tackline


java.util.Collections$SynchronizedCollection is non-public class.

like image 1
KV Prajapati Avatar answered Oct 22 '22 23:10

KV Prajapati