The first function Function<ApplyService, Class<? extends ApplyService>> f1 = ApplyService::run; is equivalent to :
Function<ApplyService, Class<? extends ApplyService>> f1 = apService -> apService.run();
So, while passing an object of type ApplyService1 the implemented method in ApplyService1 is invoked.
But the second function Function<ApplyService, Class<? extends ApplyService>> f2 = Abc::run2; is equivalent to :
Function<ApplyService, Class<? extends ApplyService>> f2 = apService -> Abc.run2(apService);
Since Abc::run2 is static method reference, the input ApplyService object is passed to the run2 method.
The first function Function<ApplyService, Class<? extends ApplyService>> f1 = ApplyService::run; is equivalent to :
Function<ApplyService, Class<? extends ApplyService>> f1 = apService -> apService.run();
So, while passing an object of type ApplyService1 the implemented method in ApplyService1 is invoked.
But the second function Function<ApplyService, Class<? extends ApplyService>> f2 = Abc::run2; is equivalent to :
Function<ApplyService, Class<? extends ApplyService>> f2 = apService -> Abc.run2(apService);
Since Abc::run2 is static method reference, the input ApplyService object is passed to the run2 method.
ApplyService::run is a "Reference to an instance method of an arbitrary object of a particular type" as described here. Notice how run is an instance method, so you would need an instance to call it. You didn't specify any instance of ApplyService in the method reference (you just said ApplyService::run), so instead, when you invoke the method reference, the first argument you pass to apply is used as the instance on which to call the instance method.
In this case, new ApplyService1() is the instance on which you call run, so when you do:
f1.apply(new ApplyService1())
you are actually calling:
new ApplyService1().run()
Notice how the argument becomes the instance on which you call the method.
In the case of Abc::run2, it is a "Reference to a static method", so none of that "parameter becoming the instance on which you call the method" shenanigan happens, and it works as you expect.
One usage of Function is in Streams. Everyone uses map method these days, I believe:
This map method accepts the Function as a parameter. This allows writing a pretty elegant code - something that could not be achieved before Java 8:
Stream.of("a", "b", "c")
.map(s -> s.toUpperCase())
.collect(Collectors.toList());
// List of A, B, C
Now its true that there are method references and functional interfaces (one of which is Function of course), this lets you using method reference to rewrite the above example as:
Stream.of("a", "b", "c")
.map(String::toUpperCase)
.collect(Collectors.toList())
... but that's only a syntactic sugar - map still accepts the Function as a parameter of course.
Another example that uses Function from Java itself is StackWalker:
Here is an example:
List<StackFrame> frames = StackWalker.getInstance().walk(s ->
s.dropWhile(f -> f.getClassName().startsWith("com.foo."))
.limit(10)
.collect(Collectors.toList()));
}
Note the call to walk method - it accepts a function as a parameter.
So bottom line, it's just yet another tool that can help the programmer to express his/her intentions. Use it wisely wherever appropriate.
Suppose I want to write an applyTwice function:
double applyTwice(double x, Function<Double, Double> f) {
return f.apply(f.apply(x));
}
This needs the function be represented as an object.
Functions are useful when you want to put some structure around arbitrary code supplied by the caller.