Определите, является ли выражение лямбда неактивным или состоящим в Java
Есть ли функция, которая принимает ссылку на выражение лямбда и возвращает логическое выражение, является ли выражение лямбда апатридом или нет? Как можно определить состояние лямбда-выражения?
Ответы
Ответ 1
Ну, выражение лямбда - это всего лишь экземпляр специального анонимного класса, который имеет только один метод. Анонимные классы могут " захватывать " переменные, находящиеся в окружении. Если ваше определение класса с состоянием - это тот, который содержит изменчивые вещи в своих полях (в противном случае это в значительной степени просто константа), тогда вам повезло, потому что то, как захват, похоже, реализован. Вот небольшой эксперимент:
import java.lang.reflect.Field;
import java.util.function.Function;
public class Test {
public static void main(String[] args) {
final StringBuilder captured = new StringBuilder("foo");
final String inlined = "bar";
Function<String, String> lambda = x -> {
captured.append(x);
captured.append(inlined);
return captured.toString();
};
for (Field field : lambda.getClass().getDeclaredFields())
System.out.println(field);
}
}
Результат выглядит примерно так:
private final java.lang.StringBuilder Test$$Lambda$1/424058530.arg$1
Ссылка StringBuilder
превратилась в поле анонимного класса лямбда (и final String inlined
константа final String inlined
была встроена для повышения эффективности, но это не относится к точке). Поэтому эта функция должна выполняться в большинстве случаев:
public static boolean hasState(Function<?,?> lambda) {
return lambda.getClass().getDeclaredFields().length > 0;
}
EDIT: как указано в @Federico, это поведение, специфичное для реализации, и может не работать в некоторых экзотических средах или будущих версиях JVM Oracle/OpenJDK.
Ответ 2
Нет, это вообще не возможно. Предлагаемый подход к проверке того, принадлежит ли лямбда классу с полем, является следующим лучшим, но наличие поля не совпадает с состоянием.
class Stateless {
int result = 0;
public int getResult() { return result; }
}
Можно доказать состояние, обнаружив две входные последовательности, для которых данная входная комбинация возвращает другой результат. Однако невозможно доказать, что такая входная последовательность не существует (любая входная последовательность может приводить к другому результату, если она была добавлена другим вызовом).
(Даже если вы проверите значения полей, найденных с помощью отражения, они могут измениться, не влияя на результат лямбда, поэтому на самом деле не делают это с точки зрения состояния).
Вот короткий компилятивный пример, показывающий как ложные положительные, так и отрицательные, опровергая понятие:
public class StatefulLambda {
static AtomicInteger counter = new AtomicInteger();
public static void main(String[] args) {
// false negative: will return different result each call
System.out.println(hasState(i -> counter.incrementAndGet()));
// false positive: will always return the same result
Object object = new Object() {
final int i = 0;
};
System.out.println(hasState(i -> object.toString()));
}
private static boolean hasState(Function<?,?> lambda) {
return lambda.getClass().getDeclaredFields().length > 0;
}
}
Ответ 3
Вот простая и глупая идея. Просто проверьте, есть ли у вас лямбда поля.
Например, рассмотрите следующие состояния лямбда.
List<Integer> serialStorage = new ArrayList<>();
Function<? super Integer, ? extends Integer> statefulLambda =
e -> { serialStorage.add(e); return e; };
Это statefulLambda
имеет частное конечное внутреннее поле arg$1
которое, очевидно, ссылается на serialStorage
. Так
statefulLambda.getClass().getDeclaredFields().length > 0
может использоваться в качестве индикатора, что лямбда является состоятельной.
Однако я понятия не имею, будет ли это вообще работать.
Ответ 4
Я бы сказал, что невозможно написать функцию, которая может определить, является ли лямбда апатридом или нет:
Если вы ищете пример метода filter
Stream API, javadoc заявляет, что этот параметр должен быть "предикатом [...] без сохранения состояния", а также ссылки на определение API без состояния.
Если бы был способ определить, был ли параметр filter
(или любого другого) неактивным или нет, класс Stream включил бы возможность выбросить исключение IllegalArgumentException в том случае, если этот параметр был включен в lambda. Поскольку это не было реализовано, и только предупреждение было добавлено в javadocs, можно сделать вывод, что нет способа написать функцию, которая может определить, является ли лямбда-лямбда безстоящей.
Редактировать (после прочтения комментариев от Эрика): Существует множество ситуаций, когда команда внедрения делает выбор реализации не реализованным для конкретной функции; мы обычно не можем заключить из тех вариантов, которые эта функция невозможна для кого-то другого. В этом специальном случае я считаю его неправдоподобным, что дизайнеры Java 8 не нашли бы и не сделали бы это, если бы было (недорогое) решение.