Есть ли способ сравнить lambda?

Скажем, у меня есть список объектов, которые были определены с использованием lambda-выражений (замыканий). Есть ли способ проверить их, чтобы их можно было сравнить?

Код, который меня больше всего интересует, это

List strategies = getStrategies(); Strategy a = (Strategy) this::a; if (strategies.contains(a)) { // ... 

Полный код

 import java.util.Arrays; import java.util.List; public class ClosureEqualsMain { interface Strategy { void invoke(/*args*/); default boolean equals(Object o) { // doesn't compile return Closures.equals(this, o); } } public void a() { } public void b() { } public void c() { } public List getStrategies() { return Arrays.asList(this::a, this::b, this::c); } private void testStrategies() { List strategies = getStrategies(); System.out.println(strategies); Strategy a = (Strategy) this::a; // prints false System.out.println("strategies.contains(this::a) is " + strategies.contains(a)); } public static void main(String... ignored) { new ClosureEqualsMain().testStrategies(); } enum Closures {; public static  boolean equals(Closure c1, Closure c2) { // This doesn't compare the contents // like others immutables eg String return c1.equals(c2); } public static  int hashCode(Closure c) { return // a hashCode which can detect duplicates for a Set } public static  String asString(Closure c) { return // something better than Object.toString(); } } public String toString() { return "my-ClosureEqualsMain"; } } 

Кажется, единственное решение – определить каждую лямбду как поле и использовать только эти поля. Если вы хотите распечатать вызванный метод, вам лучше использовать Method . Есть ли лучший способ с lambda-выражениями?

Кроме того, можно ли распечатать лямбду и получить что-то читаемое человеком? Если вы напечатаете this::a вместо

 ClosureEqualsMain$$Lambda$1/821270929@3f99bd52 

получить что-то вроде

 ClosureEqualsMain.a() 

или даже использовать this.toString и метод.

 my-ClosureEqualsMain.a(); 

3 Solutions collect form web for “Есть ли способ сравнить lambda?”

Этот вопрос может быть истолкован относительно спецификации или реализации. Очевидно, реализации могут измениться, но вы можете захотеть переписать свой код, когда это произойдет, поэтому я отвечу на обоих.

Это также зависит от того, что вы хотите сделать. Вы хотите оптимизировать или ищете надежные гарантии того, что два экземпляра (или не являются) одной и той же функцией? (Если последнее, вы столкнетесь с вычислительной физикой, в том, что даже проблемы, столь же простые, как вопрос о том, могут ли две функции вычислять одну и ту же вещь, неразрешимы.)

С точки зрения спецификации языковая спецификация обещает только, что результат вычисления (не вызывая) lambda-выражения является экземпляром classа, реализующего целевой функциональный интерфейс. Он не дает никаких обещаний относительно идентичности или степени сглаживания результата. Это по дизайну, чтобы обеспечить максимальную гибкость реализации, чтобы предлагать лучшую производительность (так как lambdas может быть быстрее, чем внутренние classы, мы не привязаны к ограничению «должен создать уникальный экземпляр», что и внутренние classы).

Так что в принципе, спецификация не дает вам многого, кроме, очевидно, что два lambdas, которые являются ссылочными (==), собираются вычислить одну и ту же функцию.

С точки зрения реализации вы можете сделать еще немного. Существует (в настоящее время может измениться) соотношение 1: 1 между синтетическими classами, которые реализуют lambda, и местами захвата в программе. Таким образом, два отдельных бита кода, которые захватывают «x -> x + 1», могут быть сопоставлены с разными classами. Но если вы оцениваете одну и ту же лямбду на том же участке захвата и что lambda не захватывает, вы получаете тот же экземпляр, который можно сравнить с эталонным равенством.

Если ваши лямбды становятся сериализуемыми, они легче сдадут свое состояние в обмен на жертву какой-то производительности и безопасности (без бесплатного обеда).

Одна из областей, где было бы целесообразно настроить определение равенства, – это ссылки на методы, поскольку это позволит им использоваться в качестве слушателей и быть должным образом незарегистрированным. Это рассматривается.

Я думаю, что вы пытаетесь добиться: если два lambda преобразуются в один и тот же функциональный интерфейс, они представлены одной и той же функцией поведения и имеют одинаковые захваченные аргументы, они одинаковы

К сожалению, это трудно сделать (для несериализуемых lambdas, вы не можете получить на всех его компонентах) и недостаточно (потому что два отдельно скомпилированных файла могут преобразовать одну и ту же лямбду в один и тот же тип функционального интерфейса, и вы бы не могу сказать.)

EG обсудила вопрос о том, следует ли предоставлять достаточную информацию для принятия этих суждений, а также обсуждать, следует ли lambdas применять более выборочные equals / hashCode или более описательные toString. Вывод состоял в том, что мы не желали платить что-либо по стоимости исполнения, чтобы сделать эту информацию доступной для вызывающего абонента (плохой компромисс, наказывая 99,99% пользователей за то, что выгодно 0,01%).

Окончательный вывод о toString не был достигнут, но остался открытым для пересмотра в будущем. Однако по этому вопросу были высказаны хорошие аргументы в пользу обеих сторон; это не шлепанье.

Я не вижу возможности, чтобы получить информацию от самого закрытия. Закрытие не обеспечивает состояние.

Но вы можете использовать Java-Reflection, если вы хотите проверить и сравнить методы. Конечно, это не очень красивое решение из-за производительности и исключений, которые нужно поймать. Но таким образом вы получаете эту метаинформацию.

Для сравнения labmdas я обычно позволяю интерфейсу расширять Serializable а затем сравнивать сериализованные байты. Не очень приятно, но работает в большинстве случаев.

  • Как выполнить соединение между несколькими таблицами в LINQ lambda
  • преобразование .net Func в .net Expression <Func >
  • ключевое слово delegate или lambda
  • Как я могу удалить CHECKED исключения из внутренних streamов Java 8?
  • Java8: двусмысленность с lambdas и перегруженные методы
  • Мутирование дерева выражений предиката для задания другого типа
  • Как определить функцию lambda для копирования копии вместо ссылки на C #?
  • Как удалить обработчик события lambda
  • Скопируйте stream, чтобы избежать «того, что stream уже оперирован или закрыт»
  • Захват переменной lambda в цикле - что здесь происходит?
  • Что такое lambda-выражение в C ++ 11?
  • Давайте будем гением компьютера.