2012-04-30 1 views
1

Я пытаюсь использовать Clojure's (bean obj) для извлечения неизменяемой карты, связанной с объектом.Проверка экземпляров непубличных классов с использованием java.bean API

В стандартной библиотеке Clojure 1.4.0, это реализовано примерно как так (перевод в псевдокоде предназначен доступным для людей, незнакомых с Clojure):

import java.beans.PropertyDescriptor; 
import java.beans.Introspector; 

function introspect(Object obj) { 
    Class clazz = obj.getClass(); 
    PropertyDescriptor descriptors[] = 
    Introspector 
    .getBeanInfo(clazz) 
    .getPropertyDescriptors(); 
    Map retval = new HashMap(); 

    for(pd in descriptors) { 
    name = pd.getName(); 
    method = pd.getReadMethod(); 
    if(method.getParameterTypes().length != 0) 
     continue; 
    retval.set(name, method.invoke(obj, nil)); 
    } 
    /* the real implementation does more magic below here, 
    but the above is sufficient for this question */ 
    return retval; 
} 

По большей части, это работает отлично - java.bean.Introspector не возвращает непубличные методы в своей реализации по умолчанию BeanInfo. Однако, когда проверяемый объект является экземпляром непубличного класса, он возвращает общедоступные методы в этом классе, даже если они не могут быть вызваны, не поднимая IllegalArgumentException («Нельзя вызвать общедоступный метод не- публичный класс ").

Как это можно исправить? Я просматриваю документацию для java.lang.Class, и я не вижу очевидного способа определить разрешения класса, которые не связаны с блоком try/catch для java.lang.SecurityException ... что не совсем похоже на меня лучшая практика. Более того, в случае, когда метод в непубличном классе реализует открытый интерфейс, должен быть доступен какой-то механизм, чтобы определить, что этот метод можно безопасно назвать.

ответ

2

Вы можете обнаружить модификаторы на классе, так что-то, как это должно позволить вам проверить, является ли объект является экземпляром частного класса (не сильно протестированы)

public boolean isInstanceOfPrivateClass(Object o) { 
    return Modifier.isPrivate(o.getClass().getModifiers()); 
} 
+0

К сожалению, в случае, когда частный класс реализует открытый интерфейс, этот ответ сам по себе не является достаточным для определения того, что методы, определенные через этот интерфейс, доступны. В сочетании с возможностью идентификации интерфейса из PropertyDescriptor этого, вероятно, будет достаточно. –

+0

Если объект относится к классу, который реализует интерфейс (который является общедоступным по определению), вы должны иметь возможность вызывать методы на интерфейсе независимо от конфиденциальности класса. Все остальное - ошибка. Обратите внимание, что Java имеет много ненужного багажа, вызванного тем, как внутренние языки были добавлены к языку. Частные классы имеют смысл только как внутренние классы. В структуре вашего примера может быть что-то, что мне не хватает. – sw1nn

+0

У меня есть непубличный класс с рядом общедоступных методов. Некоторые из этих методов можно назвать публично в силу интерфейсов, реализующих класс; некоторые из этих публичных методов не могут быть вызваны, поскольку они не определяются каким-либо открытым классом или интерфейсом, но только внутри самого негосударственного класса. Реальный случай использования вождения это «com.atlassian.crowd.embedded.ofbiz.OfBizUser', из продукта Atlasian's Crowd, используемого в Jira; Я согласен с тем, что для них нет смысла публиковать метод в непубличном классе, но мой единственный контроль над этим - это подача билета, предлагающего столько же. –

1

Эта проблема может быть решена поиск дерева наследования для открытого класса или интерфейса, содержащего тот же метод. В Clojure, это может быть реализовано (хотя и с низкой производительностью) следующим образом:

(defn- public-version-of-method [^Method method] 
    "returns a Method built against a public interface or superclass 
    declaring this method, or nil if none exists" 
    (let [sig (method-sig method)] 
    (loop [current-class (. method (getDeclaringClass)) 
      pending-supers (seq (supers current-class))] 
     (if (and current-class 
       (Modifier/isPublic (.getModifiers current-class)) 
       (some (fn [x] (= sig (method-sig x))) 
         (. current-class (getDeclaredMethods)))) 
     (. current-class (getDeclaredMethod 
          (.getName method) 
          (.getParameterTypes method))) 
     (if pending-supers 
      (recur (first pending-supers) 
        (next pending-supers)) 
      nil))))) 

... и затем вызвать .invoke на (public-version-of-method m), а не m (если она возвращает значение не-ноль), или признавая, что метод не является общедоступным, если этот метод возвращает значение nil.

(Приведенный выше код был представлен вверх по потоку как часть предлагаемого патча для CLJ-978).

+1

+1 - это не должно быть так сложно :-), Для справки. Apache commons-beanutils имеет сходство: http://www.docjar.com/html/api/org/apache/commons/beanutils/MethodUtils.java. html # 771 – sw1nn

+0

@ sw1nn Спасибо, что указали на реализацию Apache Commons - это чрезвычайно полезно! –

 Смежные вопросы

  • Нет связанных вопросов^_^