我必须通过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())
,这样我们就可以在需要时轻松地更改学生类的方法和变量,而不必担心硬编码的方法名常量。
答案 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等可以为您提供更好的实施解决方案的选择。从时间角度来看,如果您坐下来键入所有内容(像以前那样乏味),您现在就可以完成了吗?令人回味的食物...