TL; DR: Возврат Predicate
непосредственно из статического метода и сохранение его для будущего использования (возможно, на карте с именами методов в качестве ключей) для устранения узкого места в скорости отражательного доступа.
public static Predicate<Object> isNull() {
return obj -> obj == null;
}
Прежде всего, важно понять, как JVM обрабатывает ссылки на методы.Это отличное объяснение в другом вопросе .Взгляните на лямбда-документ перевода - в частности, раздел захват ссылки на метод .
list.filter(String::isEmpty)
переводится как
list.filter(indy(MH(metaFactory), MH(invokeVirtual Predicate.apply), MH(invokeVirtual String.isEmpty))()))
Это означает, что Predicate
не существует во время выполнения, если вы не напишите его в своем коде.Там может быть удобный способ получить его с помощью API отражения, но, насколько я знаю, нет.Вы можете написать что-то подобное, используя динамические прокси ;Тем не менее, я думаю, что это добавило бы ненужной сложности к коду.
Вот пример из 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
- testNormal обращается к предикату через
::
оператор - testReflectionInvoke использует метод
invoke()
напрямую - testReflectionWrappedPredicate переносит метод
invoke()
на Precicate
- testReflectionReturnedPredicate использует метод, который возвращает
Predicate
, поэтому отражение вызывается только один раз
Предикаты былидля целей данного примера кэшируется, поскольку мы не проверяем скорость получения предиката, а предполагаем, что это будет сделано только один раз.
Как видно из результатов, отражение в 6 раз медленнее, чем доступпредикаты нормально.Поэтому, на мой взгляд, самый чистый / наиболее приемлемый способ сделать это - иметь методы без аргументов, которые возвращают Predicate
type вместо boolean
.
public static Predicate<Object> isNull() {
return obj -> obj == null;
}
Тест был выполнен с использованием 3 вилок, 4 итераций прогрева и 10 итераций.
Вот код, который я использовал для тестирования, если вы хотите запустить тест самостоятельно (для выполнениябенчмаркинг):
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;
}
}