Временное решение для оболочек Java Collections, ломающих отражение - PullRequest
5 голосов
/ 17 сентября 2009

Сегодня я обнаружил, что использование 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 ]

5 голосов
/ 17 сентября 2009

Причиной исключения является то, что класс, полученный при вызове

list.getClass()

в строке

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

возвращает фактический тип - java.util.Collections $ SynchronizedCollection. Класс SynchronizedCollection оказывается внутренним классом класса Collections, к которому вы не можете получить доступ, отсюда и исключение.

Лучший способ обойти это исключение - вызвать метод size в более подходящем классе / интерфейсе - обычно это класс реализации (поскольку он определенно содержит объявление или определение метода), но в нашем случае мы необходимо вызвать size () для открытого типа, учитывая, что obj.getClass () вернул непубличный тип. В частности, нам нужно вызвать size () в интерфейсе java.util.Collection (super) или в интерфейсе java.util.List.

Следующие операторы выведут «size = 1» в консоли:

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

Обновление

Если вам было интересно, синхронизирован ли метод size (), вызванный с помощью отражения, ответ - да, он синхронизирован. Несмотря на вызов метода size () для супертипа - Collection / List, метод size () во внутреннем классе SynchronizedCollection вызывается, и это происходит с синхронизацией. Это - деталь реализации, гарантированно работающая, так как коллекция упакована.

Кроме того, не рекомендуется использовать отражение, когда супертип - интерфейс Collection содержит метод size () .

3 голосов
/ 17 сентября 2009

Получите метод из Collection.class (в общем, итерируйте суперклассы (и интерфейсы), чтобы найти что-то публичное). Или просто не используйте отражение.

1 голос
/ 17 сентября 2009

java.util.Collections $ SynchronizedCollection является закрытым классом.

0 голосов
/ 17 сентября 2009

Вы уверены, выбор java.util.concurrent будет медленнее (или достаточно медленным, чтобы беспокоиться?).

Я думаю, у вас есть 2 варианта:

  1. LinkedBlockingQueue
  2. CopyOnWriteArrayList

Я думаю, что для определенных случаев использования эти реализации могут быть на быстрее . И как примечание: если вам это понадобится, коллекции в java.util.concurrent могут обрабатывать параллельные изменения.

Из Параллельных коллекций раздела "Параллелизм Java на практике":

CopyOnWriteArrayList является одновременным замена для синхронизированного списка который предлагает лучший параллелизм в некоторых общие ситуации и устраняет нужно заблокировать или скопировать коллекцию во время итерации. (Так же, CopyOnWriteArraySet является одновременным замена для синхронизированного набора.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...