Являются ли ссылки на методы Java стабильными?

Если я получаю ссылку на метод с использованием нового синтаксиса:

anObject::aMethod

Всегда ли получаю один и тот же объект? То есть, могу ли я полагать, что две ссылки на один и тот же метод будут равны?

Это полезно знать, если, например, я планирую использовать их как обратные вызовы Runnable которые я могу добавить и удалить:

someLibrary.addCallback(anObject::aMethod)
// later
someLibrary.removeCallback(sameObject::sameMethod)

Будет ли это требовать сохранения ссылки в переменной Runnable чтобы сохранить ее стабильной?

Ответ 1

JLS не дает никаких обещаний относительно идентичности или равенства того, что вы получаете из ссылочных выражений метода.

Вы можете выполнить быстрый тест:

Object obj = new Object();

IntSupplier foo = obj::hashCode;
IntSupplier bar = obj::hashCode;

System.out.println(foo == bar);  // false

System.out.println(foo.equals(bar));  // false      

Но это, конечно, зависит от реализации.

Вы можете сделать свой лямбда Serializable и перечислить свою карту обратного вызова с серллиализированным представлением. См. Как сериализовать лямбда? , Хотя это будет работать, это не обязательно требуется для работы по спецификациям.

Ответ 2

Просто попробуйте это, чтобы получить ответ:

Object object = ...;
Supplier<String> s1 = object::toString;
Supplier<String> s2 = object::toString;
System.out.println(s1.equals(s2));

И ответ: к сожалению, нет.

Конечно, если вы сохраните одну и ту же ссылку (т.е. тот же объект), она будет работать; но если в качестве примера выше вы запрашиваете два лямбда, хотя они кажутся одинаковыми, они никогда не будут равны. Поэтому reference = object::method а затем позже remove(reference), очевидно, будет работать, но remove(sameObject::sameMethod) из коллекции никогда не будет работать, если он написан буквально как таковой.

Ответ также нет для конструктора (например, ArrayList :: new) и несвязанных методов (например, Object :: toString). Кажется, что новая лямбда строится каждый раз, когда вы используете выражение лямбда.

Как указывает @Hitobat, это неравенство имеет смысл, если вы думаете о том, что именно лямбды и откуда они взялись. В основном Supplier<String> x = myObject::toString является синтаксическим советом для Supplier<String> x = new Supplier<String>(... ). Без надлежащей перегрузки Object.equals два экземпляра анонимного класса, очевидно, различны. Как многие люди, вероятно, я, хотя, что есть какой-то кеш часто используемых лямбда где-то, чтобы сделать его более эффективным; ну, совсем нет.