[英]How can I get Predicate from static methods via reflection?
Given a class consisting of static predicate methods, I want to access them via reflection and convert them to Predicate<Object>
type. 给定一个包含静态谓词方法的类,我想通过反射访问它们并将它们转换为
Predicate<Object>
类型。
public class MyPredicates {
public static boolean isNull(Object obj) {
return obj == null;
}
public static boolean hasNegativeHashcode(Object obj) {
return obj.hashCode() < 0;
}
}
Normally, I would write the following code to get the predicate: 通常,我将编写以下代码来获取谓词:
Predicate<Object> isNull = MyPredicates::isNull;
However, I don't know how to do that using reflection. 但是,我不知道如何使用反射来做到这一点。 My intention is to create an annotation for these methods and get them via reflection to create a list of available predicates for my framework.
我的意图是为这些方法创建一个注释,并通过反射获得它们,以为我的框架创建可用谓词的列表。
I thought of three possible approaches, none of which seems good to me. 我想到了三种可能的方法,但对我来说似乎都不是一件好事。
Method
, call invoke()
and cast the returned Object
to boolean
. Method
一样保留它,调用invoke()
并将返回的Object
为boolean
。 This, however, would be hard to read and it would mean that I'd have no way of checking the type during runtime. Predicate
but this would involve additional overhead. Predicate
但这会涉及额外的开销。 In any case, I fear that using reflection directly will add more overhead and slow down the program. 无论如何,我担心直接使用反射会增加开销并减慢程序速度。
So, my questions are: 因此,我的问题是:
Predicate
via reflection directly? Predicate
吗? Predicate
)? Predicate
)访问这种方法而又不增加过多开销的合适方法是什么? TL;DR: Return Predicate
directly from a static method and store it for future use (possibly in a map with method names as keys) to eliminate speed bottleneck of reflective access. TL; DR:直接从静态方法返回
Predicate
并将其存储以备将来使用(可能在以方法名称作为键的映射中),以消除反射访问的速度瓶颈。
public static Predicate<Object> isNull() {
return obj -> obj == null;
}
First of all, it's important to understand how JVM handles method references. 首先,了解JVM如何处理方法引用很重要。 There is a great explanation of that in another question .
在另一个问题中对此有很好的解释 。 Take a look at the lambda translation document - in particular, section Method reference capture .
查看lambda翻译文档 -特别是方法参考捕获部分。
list.filter(String::isEmpty)
is translated as
被翻译成
list.filter(indy(MH(metaFactory), MH(invokeVirtual Predicate.apply), MH(invokeVirtual String.isEmpty))()))
This means that Predicate
doesn't exist during runtime unless you write it in your code. 这意味着除非您在代码中编写
Predicate
,否则在运行时该Predicate
不存在。 There could be a convenient way to get it via reflection API but as far as I know, there isn't. 通过反射API可能有一种方便的方法,但是据我所知,还没有。 You may be able to write something similar using dynamic proxies ;
您也许可以使用动态代理编写类似的东西; however, I think it would add unnecessary complexity to the code.
但是,我认为这会给代码增加不必要的复杂性。
Here is a benchmark of 4 different methods to achieve the desired behaviour, including those mentioned in the question: 这是实现所需行为的4种不同方法的基准,包括问题中提到的那些方法:
Benchmark Mode Cnt Score Error Units
MyBenchmark.testNormal thrpt 30 362.782 ± 13.521 ops/us
MyBenchmark.testReflectionInvoke thrpt 30 60.440 ± 1.394 ops/us
MyBenchmark.testReflectionWrappedPredicate thrpt 30 62.948 ± 0.846 ops/us
MyBenchmark.testReflectionReturnedPredicate thrpt 30 381.780 ± 5.265 ops/us
::
operator ::
运算符访问谓词 invoke()
method directly invoke()
方法 invoke()
method to Precicate
invoke()
方法包装为Precicate
Predicate
, so reflection is invoked only once Predicate
的方法,因此反射仅被调用一次 The predicates were cached for the purpose of this example, since we are not testing the speed of getting a predicate but assuming it'll be done only once. 出于本示例的目的,已对谓词进行了缓存,因为我们不是在测试获取谓词的速度,而是假定只会执行一次。
As you can see from the results, reflection is 6 times slower than accessing predicates normally. 从结果中可以看到,反射比正常访问谓词慢6倍。 Therefore, in my opinion, the cleanest/most maintainable way to do this is to have methods with no arguments which return
Predicate
type instead of boolean
. 因此,我认为, 最干净/最可维护的方法是使方法不包含返回
Predicate
类型而不是boolean
。
public static Predicate<Object> isNull() {
return obj -> obj == null;
}
The benchmark has been done using 3 forks, 4 warm-up iterations and 10 iterations. 使用3个fork,4个热身迭代和10个迭代来完成基准测试。
Here is the code I used for testing, if you want to run the benchmark yourself (uses JMH framework to do the benchmarking): 如果您想自己运行基准测试,请使用以下代码进行测试(使用JMH框架进行基准测试):
public class MyBenchmark {
public static Predicate<Object> normal = MyBenchmark::isNull;
public static Method reflectionInvoke;
public static Predicate<Object> reflectionWrappedPredicate;
public static Predicate<Object> reflectionReturnedPredicate;
static {
try {
Method m1 = MyBenchmark.class.getMethod("isNull", Object.class);
reflectionInvoke = m1;
reflectionWrappedPredicate = a -> {
try {
return (boolean)m1.invoke(null, a);
}
catch(IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
return false;
}
};
Method m2 = MyBenchmark.class.getMethod("isNull");
reflectionReturnedPredicate = (Predicate)m2.invoke(null);
}
catch(IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException ex) {
ex.printStackTrace();
};
}
@Benchmark
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean testNormal() {
Predicate<Object> p = normal;
return p.test(this) | p.test(null);
}
@Benchmark
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean testReflectionInvoke() {
try {
Method m = reflectionInvoke;
return (boolean)m.invoke(null, this) | (boolean)m.invoke(null, (Object)null);
}
catch(IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException ex) {
ex.printStackTrace();
return false;
}
}
@Benchmark
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean testReflectionWrappedPredicate() {
Predicate<Object> p = reflectionWrappedPredicate;
return p.test(this) | p.test(null);
}
@Benchmark
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean testReflectionReturnedPredicate() {
Predicate<Object> p = reflectionReturnedPredicate;
return p.test(this) | p.test(null);
}
public static boolean isNull(Object obj) {
return obj == null;
}
public static Predicate<Object> isNull() {
return obj -> obj == null;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.