Являются ли ссылки на методы 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 два экземпляра анонимного класса, очевидно, различны. Как многие люди, вероятно, я, хотя, что есть какой-то кеш часто используемых лямбда где-то, чтобы сделать его более эффективным; ну, совсем нет.