简体   繁体   English

如何通过反射从静态方法获取谓词?

[英]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. 我想到了三种可能的方法,但对我来说似乎都不是一件好事。

  1. I could leave it like Method , call invoke() and cast the returned Object to boolean . 我可以像Method一样保留它,调用invoke()并将返回的Objectboolean This, however, would be hard to read and it would mean that I'd have no way of checking the type during runtime. 但是,这将很难阅读,这意味着我无法在运行时检查类型。
  2. I could wrap the reflection call to Predicate but this would involve additional overhead. 我可以将反射调用包装到Predicate但这会涉及额外的开销。
  3. I could make the user to register every method separately (hard to maintain when adding/removing many methods). 我可以让用户分别注册每个方法(添加/删除许多方法时很难维护)。

In any case, I fear that using reflection directly will add more overhead and slow down the program. 无论如何,我担心直接使用反射会增加开销并减慢程序速度。

So, my questions are: 因此,我的问题是:

  1. Can I get the Predicate via reflection directly? 我可以直接通过反射获得Predicate吗?
  2. If not, what would be an appropriate way of accessing such methods without adding too much overhead, while having a usable API (eg by involving Predicate )? 如果没有,那么在拥有可用的API的同时(例如通过涉及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
  1. testNormal accesses the predicate via :: operator testNormal通过::运算符访问谓词
  2. testReflectionInvoke uses the invoke() method directly testReflectionInvoke直接使用invoke()方法
  3. testReflectionWrappedPredicate wraps the invoke() method to Precicate testReflectionWrappedPredicateinvoke()方法包装为Precicate
  4. testReflectionReturnedPredicate uses a method which returns Predicate , so reflection is invoked only once testReflectionReturnedPredicate使用返回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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM