如何获取特定的方法名称作为字符串,而不是对方法名称进行硬编码

时间:2018-08-07 09:22:44

标签: java reflection

我必须通过Java反射调用特定的方法。除了传递硬编码的方法名称之外,还可以将方法名称作为字符串传递吗?

例如

 public String getAttribute(Object object1, Object2, String className, String methodName){
     Class<?> clazz = Class.forName(className);
     Method method = clazz.getMethod(methodName);
     return ObjectUtils.firstNonNull(null == object1 ? null: method.invoke(object1),null == object2 ? null: method.invoke(object2); }

让我们说我上课

 @Getter
 @Setter 
 Class Student{
   String studentName;
   String address;
   int rollNumber;
 }

可以说,我们有呼叫者代码

Student student1 = new Student();// Student record from School 1
Student student2 = new Student(); // Student record from School 2
student2.setAddress("ABC");
System.out.println(getAttribute(student1, student2, Student.class.name(), "getAddress"));

不是将硬编码的方法名称作为参数传递给getAttribute()方法,有没有办法我可以使用非硬编码的方法名称?

例如getAttribute(student, Student.class.name(), Student.class.getStudentName.getName()),这样我们就可以在需要时轻松地更改学生类的方法和变量,而不必担心硬编码的方法名常量。

3 个答案:

答案 0 :(得分:5)

要查找集合中对象的给定getter的第一个非空结果,可以使用流,方法引用和可选方法,同时完全避免反射。

public static <T, R> Optional<R> findFirstNonNull(Collection<T> objects, 
                                                  Function<T, R> getter) {
    return objects.stream()
            .filter(Objects::nonNull)
            .map(getter)
            .filter(Objects::nonNull)
            .findFirst();
}

用法示例:Optional<String> found = findFirstNonNull(fooList, Foo::getName);

public class Foo {

    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public static void main(String[] args) {
        Foo foo1 = null;
        Foo foo2 = new Foo();
        Foo foo3 = new Foo();
        foo3.setName("foo3");
        Foo foo4 = new Foo();
        foo4.setName("foo4");
        List<Foo> fooList = Arrays.asList(foo1, foo2, foo3, foo4);
        Optional<String> found = findFirstNonNull(fooList, Foo::getName);
        System.out.println(found); // Optional[foo3]
    }
}

注意:这些是Java 8功能。

答案 1 :(得分:0)

您可以在运行时访问注释。通过使用批注标记要与反射一起使用的方法,可以获取所有方法,然后运行带有批注的方法。

以下是一个很好的例子:java-seek-a-method-with-specific-annotation-and-its-annotation-element

Student student1 = new Student();// Student record from School 1
Student student2 = new Student(); // Student record from School 2
student2.setAddress("ABC");
try {
    System.out.println(getAttribute(student1));
} catch (Exception e) {
    System.out.println("Some error");
}

public static String getAttribute(Object object) throws Exception{
    Method method = getAnnotatedMethod(object.getClass());
    return (String) method.invoke(object);
}

public static Method getAnnotatedMethod(final Class<?> type) {
    final List<Method> allMethods = new ArrayList<Method>(Arrays.asList(type.getMethods()));
    for (final Method method : allMethods) {
        if (method.isAnnotationPresent(RunWithReflection.class)) {
            return method; 
        } 
    }
    return null;
}

然后您需要注释:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class anno {

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RunWithReflection {
    }
}

然后您只需使用@RunWithReflection注释要运行的函数,即可使用。

答案 2 :(得分:0)

另一种可能性是用每个属性的访问者创建一个枚举,例如:

public enum StudentAttribute {
    NAME,
    ADDRESS,
    ROLLNUMBER,
    ;

    public Object get(Student s) {
        switch(this) {
            case NAME: return s.getName();
            case ADDRESS: return s.getAddress();
            case ROLLNUMBER: return s.getRollNumber();
        }
    }
}

...

public Object getAttribute(StudentAttribute attr, Student... students) {
    if(students==null) return null;
    return Arrays.stream(students) //Java 8 Stream
        .map(attr::get) //convert into the corresponding attribute value
        .filter(o -> o!=null) //we're only interested in non-null values
        .findFirst() //specifically the first such non-null value
        .orElse(null) //otherwise null
        ;
}


//access:
Student student1 = new Student();// Student record from School 1
Student student2 = new Student(); // Student record from School 2
student2.setAddress("ABC");
System.out.println(getAttribute(StudentAttribute.ADDRESS, student1, student2));

如果将属性(调用方法)作为字符串传递给main方法,则可以例如传递“ ADDRESS”(按枚举常量精确命名)并执行:

public static void main(String[] args) {
    Student s1 = ...
    Student s2 = ...
    ...
    StudentAttribute attr = StudentAttribute.valueOf(args[0]); //this will be StudentAttribute.ADDRESS
    System.out.println(getAttribute(attr, student1, student2));

这里没有任何硬编码的字符串,也没有反射,因此可以在您想到的任何类型的重构中幸免。 但是,与其他解决方案一样,这不必要地冗长,因为您或多或少地复制了代码,以便以后能够对其进行重构。我认为完全不必要的重复更多是代码味道,而不是必须多次键入“ getAddress”,并且已经需要立即重构为更简单的内容。

也许使用Streams,varargs等可以为您提供更好的实施解决方案的选择。从时间角度来看,如果您坐下来键入所有内容(像以前那样乏味),您现在就可以完成了吗?令人回味的食物...