Отражение Java: Как я могу получить все методы получения класса Java и вызвать их - PullRequest
66 голосов
/ 15 декабря 2011

Я пишу Java-класс, который имеет много методов получения ... теперь я хочу получить все методы получения и вызывать их когда-нибудь ... Я знаю, что существуют методы, такие как getMethods () или getMethod (String name, Class ... parameterTypes) , но я просто хочу получить геттер действительно ..., использовать регулярные выражения? Кто-нибудь может сказать мне? Спасибо!

Ответы [ 6 ]

145 голосов
/ 15 декабря 2011

Не используйте регулярные выражения, используйте Introspector:

for(PropertyDescriptor propertyDescriptor : 
    Introspector.getBeanInfo(yourClass).getPropertyDescriptors()){

    // propertyEditor.getReadMethod() exposes the getter
    // btw, this may be null if you have a write-only property
    System.out.println(propertyDescriptor.getReadMethod());
}

Обычно вам не нужны свойства из Object.class, поэтому вы должны использовать метод сдва параметра:

Introspector.getBeanInfo(yourClass, stopClass)
// usually with Object.class as 2nd param
// the first class is inclusive, the second exclusive

Кстати: есть рамки, которые делают это для вас и предоставляют вам представление высокого уровня.Например, commons / beanutils имеет метод

Map<String, String> properties = BeanUtils.describe(yourObject);

( docs здесь ), который делает именно это: находит и выполняет все методы получения и сохраняет результат на карте.К сожалению, BeanUtils.describe() преобразует все значения свойств в строки перед возвращением.WTF.Спасибо @ danw


Обновление:

Вот метод Java 8, который возвращает Map<String, Object> на основе свойств объекта bean.

public static Map<String, Object> beanProperties(Object bean) {
  try {
    return Arrays.asList(
         Introspector.getBeanInfo(bean.getClass(), Object.class)
                     .getPropertyDescriptors()
      )
      .stream()
      // filter out properties with setters only
      .filter(pd -> Objects.nonNull(pd.getReadMethod()))
      .collect(Collectors.toMap(
        // bean property name
        PropertyDescriptor::getName,
        pd -> { // invoke method to get value
            try { 
                return pd.getReadMethod().invoke(bean);
            } catch (Exception e) {
                // replace this with better error handling
               return null;
            }
        }));
  } catch (IntrospectionException e) {
    // and this, too
    return Collections.emptyMap();
  }
}

Возможно, вы захотите сделать обработку ошибок более надежной.Извините за шаблон, проверенные исключения не позволяют нам работать в полную силу.


Оказывается, что Collectors.toMap () ненавидит нулевые значения.Вот более императивная версия приведенного выше кода:

public static Map<String, Object> beanProperties(Object bean) {
    try {
        Map<String, Object> map = new HashMap<>();
        Arrays.asList(Introspector.getBeanInfo(bean.getClass(), Object.class)
                                  .getPropertyDescriptors())
              .stream()
              // filter out properties with setters only
              .filter(pd -> Objects.nonNull(pd.getReadMethod()))
              .forEach(pd -> { // invoke method to get value
                  try {
                      Object value = pd.getReadMethod().invoke(bean);
                      if (value != null) {
                          map.put(pd.getName(), value);
                      }
                  } catch (Exception e) {
                      // add proper error handling here
                  }
              });
        return map;
    } catch (IntrospectionException e) {
        // and here, too
        return Collections.emptyMap();
    }
}

Вот та же функциональность в более сжатой форме, используя JavaSlang :

public static Map<String, Object> javaSlangBeanProperties(Object bean) {
    try {
        return Stream.of(Introspector.getBeanInfo(bean.getClass(), Object.class)
                                     .getPropertyDescriptors())
                     .filter(pd -> pd.getReadMethod() != null)
                     .toJavaMap(pd -> {
                         try {
                             return new Tuple2<>(
                                     pd.getName(),
                                     pd.getReadMethod().invoke(bean));
                         } catch (Exception e) {
                             throw new IllegalStateException();
                         }
                     });
    } catch (IntrospectionException e) {
        throw new IllegalStateException();

    }
}

И вотВерсия гуавы:

public static Map<String, Object> guavaBeanProperties(Object bean) {
    Object NULL = new Object();
    try {
        return Maps.transformValues(
                Arrays.stream(
                        Introspector.getBeanInfo(bean.getClass(), Object.class)
                                    .getPropertyDescriptors())
                      .filter(pd -> Objects.nonNull(pd.getReadMethod()))
                      .collect(ImmutableMap::<String, Object>builder,
                               (builder, pd) -> {
                                   try {
                                       Object result = pd.getReadMethod()
                                                         .invoke(bean);
                                       builder.put(pd.getName(),
                                                   firstNonNull(result, NULL));
                                   } catch (Exception e) {
                                       throw propagate(e);
                                   }
                               },
                               (left, right) -> left.putAll(right.build()))
                      .build(), v -> v == NULL ? null : v);
    } catch (IntrospectionException e) {
        throw propagate(e);
    }
}
19 голосов
/ 02 декабря 2013

Вы можете использовать Reflections framework для этого

import org.reflections.ReflectionUtils.*;
Set<Method> getters = ReflectionUtils.getAllMethods(someClass,
      ReflectionUtils.withModifier(Modifier.PUBLIC), ReflectionUtils.withPrefix("get"));
9 голосов
/ 15 декабря 2011
 // Get the Class object associated with this class.
    MyClass myClass= new MyClass ();
    Class objClass= myClass.getClass();

    // Get the public methods associated with this class.
    Method[] methods = objClass.getMethods();
    for (Method method:methods)
    {
        System.out.println("Public method found: " +  method.toString());
    }
8 голосов
/ 14 апреля 2015

Spring предлагает простой метод BeanUtil для интроспекции Бина:

PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(clazz, property);
Method getter = pd.getReadMethod();
1 голос
/ 09 марта 2018

Этот код проверен ОК.

private void callAllGetterMethodsInTestModel(TestModel testModelObject) {
        try {
            Class testModelClass = Class.forName("com.example.testreflectionapi.TestModel");
            Method[] methods = testModelClass.getDeclaredMethods();
            ArrayList<String> getterResults = new ArrayList<>();
            for (Method method :
                    methods) {
                if (method.getName().startsWith("get")){
                    getterResults.add((String) method.invoke(testModelObject));
                }
            }
            Log.d("sayanReflextion", "==>: "+getterResults.toString());
        } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
0 голосов
/ 24 июня 2014

Вы должны поддерживать универсальный метод получения в каждом bean-компоненте, так что для вызова getAttribute1 () вы должны иметь возможность вызывать универсальный метод get get ("Attribute1")

Этот универсальный геттер, в свою очередь, вызовет правильный геттер

Object get(String attribute)
{
    if("Attribute1".equals(attribute)
    {
        return getAttribute1();
    }
}

Этот подход подразумевает поддержание этого отдельного списка в каждом bean-компоненте, но таким образом вы избегаете отражения, которое имеет проблемы с производительностью, поэтому, если вы пишете производственный код, который должен иметь хорошую производительность, вы можете использовать вышеуказанный шаблон для всех ваших bean-компонентов. 1006 *

Если какой-то тестовый код или служебный код не предъявляет высоких требований к производительности, то лучше использовать другие подходы, так как этот подход подвержен ошибкам, если вы не можете написать какую-либо проверку времени компиляции, которая обеспечивает работу этой универсальной функции получения для всех атрибутов.

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