Java 如何将特定的方法名作为字符串而不是硬编码方法名

Java 如何将特定的方法名作为字符串而不是硬编码方法名,java,reflection,Java,Reflection,我必须通过Java反射调用特定的方法。是否可以将方法名作为字符串传递,而不是传递硬编码的方法名 比如说 public String getAttribute(Object object1, Object2, String className, String methodName){ Class<?> clazz = Class.forName(className); Method method = clazz.getMethod(methodName);

我必须通过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); }
比方说,我们有调用方代码

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()
method之外,还有什么方法可以使用非硬编码的方法名


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

要查找集合中对象的给定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();
}
public静态可选findFirstNonNull(集合对象,
函数(吸气剂){
返回objects.stream()
.filter(对象::非空)
.map(getter)
.filter(对象::非空)
.findFirst();
}
用法示例:
optionalfound=findFirstNonNull(傻瓜,Foo::getName)

公共类Foo{
私有字符串名称;
公共void集合名(字符串名){
this.name=名称;
}
公共字符串getName(){
返回名称;
}
公共静态void main(字符串[]args){
Foo foo1=null;
Foo foo2=新的Foo();
Foo foo3=新的Foo();
foo3.集合名(“foo3”);
Foo foo4=新的Foo();
foo4.集合名(“foo4”);
List-doulist=Arrays.asList(foo1、foo2、foo3、foo4);
可选的found=findFirstNonNull(傻瓜主义者,Foo::getName);
System.out.println(找到);//可选[foo3]
}
}

注意:这些是Java 8的特性。

您可以在运行时访问注释。通过将要用于反射的方法标记为带有注释的方法,可以获得所有方法,然后运行带有注释的方法

下面是一个很好的例子:


然后您只需对要使用@RunWithReflection运行的函数进行注释,它就可以工作。

另一种可能是创建一个枚举,每个属性都有访问器,例如:

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));
如果将属性(要调用的方法)作为字符串传递到主方法中,例如,您可以传递“地址”(根据枚举常量精确命名)并执行:

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等可以为实现解决方案提供更好的选择。就时间而言,如果你坐下来把所有的东西都打印出来,你现在会不会已经完成了,就像那样乏味?值得思考的是……

Student.class.getStudentName.getName()
是不可能的。我认为这个答案也很好地回答了你的问题:
Student.class.getStudentName.getName()
如何比
的“getStudentName”
更好?我知道如果将来需要更改名称,您可以更轻松地使用重构工具,但是您是否希望避免硬编码名称?是的,我希望轻松地使用重构。什么是
@Getter
@Setter
注释?Lombok?@GangadharEnagandula对于给定的对象集合,问题是否可以归结为,您想找到给定getter的第一个非空结果?在我的例子中,我想对对象调用给定的方法,该方法名称最初没有给出,这将在运行时提供。@GangadharEnagandula将方法引用放在映射中,或使用枚举。@Vulcan和Thomas:不幸的是,我不能评论您的答案,但您没有得到他要求的结果。方法名可能会更改,因此他根本无法使用方法名。Thomas,您可以使用getAddress()和其他,您可以使用方法引用::getName。两者都不能使用。希望我有更多的名声:(
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 {
    }
}
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));
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));