Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why aren't method references singleton?

Tags:

In Java, the following code returns false on both queries. Why? Wouldn't it be simpler for method references to be singleton? It would certainly make attaching and detaching listeners a lot simpler. As it is you need to keep a constant for any method reference that will need to be equivalence checked, you can't just use the method reference operator at every necessary location.

public class Main {      public Main() {         // TODO Auto-generated constructor stub     }      public void doStuff() {      }      public static void main(String[] args) {         Main main = new Main();         Runnable thing1 = main::doStuff;         Runnable thing2 = main::doStuff;         System.out.println(thing1 == thing2); // false         System.out.println(thing1.equals(thing2)); // false     }  } 
like image 875
Dimitriye98 Avatar asked Dec 23 '14 08:12

Dimitriye98


2 Answers

For instance methods, I don't think it would make sense for them to be cached. You'd have to cache one method per instance... which would either mean an extra field within the class associated with the method - one per public method, presumably, because the methods could be referenced from outside the class - or cached within the user of the method reference, in which case you'd need some sort of per-instance cache.

I think it makes more sense for method references to static methods to be cached, because they'll be the same forever. However, to cache the actual Runnable, you'd need a cache per type that it was targeting. For example:

public interface NotRunnable {     void foo(); }  Runnable thing1 = Main::doStuff; // After making doStuff static NotRunnable thing2 = Main::doStuff; 

Should thing1 and thing2 be equal here? How would the compiler know what type to create, if so? It could create two instances here and cache them separately - and always cache at the point of use rather than the point of method declaration. (Your example has the same class declaring the method and referencing it, which is a very special case. You should consider the more general case where they're different>)

The JLS allows for method references to be cached. From section 15.13.3:

Next, either a new instance of a class with the properties below is allocated and initialized, or an existing instance of a class with the properties below is referenced.

... but even for static methods, it seems javac doesn't do any caching at the moment.

like image 87
Jon Skeet Avatar answered Oct 17 '22 18:10

Jon Skeet


In this simple case, you could do as you suggest by creating extra static or instance fields as appropriate, more complex if the lambda refers to objects, however the intent is to inline these instances out of existence e.g.

List<String> list = .... list.stream().filter(s -> !s.isEmpty()).forEach(System.out::println); 

should be as efficient (even creating no more objects than)

for (String s : list)     if(!s.isEmpty())          System.out.println(s); 

It can eliminate the objects by inlining the stream code and using escape analysis to remove the need to create objects in the first place.

For this reason there has been little focus on implementing equals(), hashCode() or toString(), accessing them via reflection for closures. AFAIK, this deliberate to avoid the objects being using in ways not intended.

like image 31
Peter Lawrey Avatar answered Oct 17 '22 17:10

Peter Lawrey