0

Say I have a small demo to count the number of times a method is called, e.g.

public class Test {

    public static Map<Function<String, Integer>, Integer> map = new HashMap<>();

    public Test() {
        map.put(this::method1, 0);
        map.put(this::method2, 0);
    }

    public Integer method1(String a) {
        Integer time = map.get(this::method1);
        map.put(this::method1, time + 1);
        return time;
    }

    public Integer method2(String a) {
        Integer time = map.get(this::method2);
        map.put(this::method2, time + 1);
        return time;
    }
}

The code above demoed the idea, but the code doesn't compile. It does not complain at the map.put(); it complains at the map.get() parts. Do you have an explanation? As well as a way to fix this (while still using function objects and a map, not two individual integers to do the counting).

1
  • 3
    What's the parameter type of Map#get? Commented Feb 27, 2017 at 3:15

2 Answers 2

4

You can make this compile by casting the function in the get method:

map.get((Function<String, Integer>)this::method2);

But the code still won't work.

Each lambda expression creates a new function class, and since Function does not implement hashcode and equals, the only way you could use them as map keys is if you use the same instance/object for the insertion and lookup. That said, I can't think of a reason why you would ever want to use a function as a map key.

Sign up to request clarification or add additional context in comments.

1 Comment

Yes you are right, got it compiled but can't lookup the methods...to bad.
1

As @SotiriosDelimanolis hinted, Map.get() accepts Object, not the key type, so the compiler is unable to infer the target lambda type. There are a few possible workarounds.

  1. Create a temporary variable:

    Function<String, Integer> key = this::method1;
    Integer time = map.get(key);
    
  2. Cast the lambda to the target type:

    Integer time = map.get((Function<String, Integer>)this::method1);
    
  3. Use an overload that does accept the key type, like merge(). This also improves your code by merging the get and put into one statement, and possibly making the constructor initialization unnecessary (did you really mean to initialize to 1?):

    map.merge(this::method1, 1, Integer::sum);
    

2 Comments

Haha, no, it was a typo. But yeah, good information there you provided. But sadly the function objects was not the same and the map could not look them up...
@user1589188 You could cache the method references outside the method instead of recreating them inside. But it's probably even simpler to just use a string or enum key, since there's nothing really tying the method reference to the executing method (that would only be possible by digging though the stack trace).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.