How exactly do you chain method references for instances with Java 8? Example:
Collections.sort(civs,Comparator.comparing(Civilization::getStrategy.getStrategLevel));
getStrategy of a Civilization instance returns a Strategy object instance which has the instance method getStrategyLevel.
Why doesn't the Comparator.comparing method return a comparator with it's functional interface implemented by the lambda expression?
In that case, you should use a lambda, you can't apply a method reference directly:
Collections.sort(civs, Collectors.comparing(c -> c.getStrategy().getStrategLevel()));
Though, there is a way to use a method reference here. Assuming that you have a class like
class CivilizationUtils {
public static Integer getKeyExtractor(Civilization c) {
return c.getStrategy().getStrategLevel();
}
}
the issue could be solved like
Collections.sort(civs, Collectors.comparing(CivilizationUtils::getKeyExtractor));
You cannot do it with a method reference, you need to use a lambda expression or create a static method.
There are four kinds of method references:
Reference to a static method like ContainingClass::staticMethodName
Reference to an instance method of a particular object like containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type like ContainingType::methodName
Reference to a constructor like ClassName::new
More details about method reference.
So here, with a lambda expression it would be:
Collections.sort(civs, Comparator.comparing(c -> c.getStrategy.getStrategLevel()));
Or in case you create a static method
public static int getStrategLevel(Civilization c) {
return c.getStrategy().getStrategLevel();
}
Then your code would be:
Collections.sort(civs, Comparator.comparing(MyClass::getStrategLevel));
Collections.sort(civs,Comparator.comparing(civ -> civ.getStrategy().getStrategLevel()));
Different example, but I have a method
void m(Predicate<String> stringPredicate)
and a utility class
class Utilities {
static boolean condition1(String s) { ... }
static boolean condition2(String s) { ... }
...
}
and I wanted to invoke m with a predicate that returns true iff Utilities.condition1 returns false. The Java grammar allows me to write
m(Utilities::condition1)
but not
m(Utilities::condition1.negate())
(an unfortunate violation of referential transparency), and the compiler complained, "Java Method reference not expected here."
My workaround was to write a method
Predicate<String> not(Predicate<String> p) {
return p;
}
and then to write the call
m(not(Utilities::condition1))
--which is allowed by the Java grammar.
Related
Studying "cracking the coding interview" in Java, on page 51 I came across:
void permutation(String str){
permutation(str,"");
}
void permutation(String str, String prefix){
if(str.length()==0){
System.out.println(prefix);
} else{
for(int i=0;i<str.length();i++){
String rem=str.substring(0,i)+str.substring(i+1);
permutation(rem,prefix+str.charAt(i));
}
}
}
I get that the first permutation function takes a string and calls the second permutation function which does all the work. However, isn't the second permutation a redeclaration of the first permutation function? How will Java recognize and use the first permutation function and not overwrite it?
How will java recognize and use the first permutation function?
When you call the method, Java will see what you're trying pass into it. Based on the arguments you pass, it will decide which 'version' of the method you are trying to use.
Like others have said - this is method overloading
Unlike in Python, in Java these two declarations live side-by-side -- the second doesn't replace the first. In Java, the rule is roughly that when you call a method with multiple definitions (aka an "overloaded" method), Java will look for the one that best matches the arguments you called it with and run that method. So permutation("hi") invokes the first version, and permutation("hi", "") calls the second.
The fundamental difference here is that in Python you can imagine the interpreter reading the definitions one at a time and replacing its overall definition of permutation every time it gets a new definition. In Java, you have to think of it as reading all the definitions of permutation at once, and calling the most appropriate one for any given invocation.
(A consequence of this is that Java also checks at compile-time that every overloaded version of a method is callable: for instance, if you wrote two versions of permutation that both took just a string as their argument, the compiler would give you an error and wouldn't compile your program at all. In python you'd just get the second definition.)
To explain what the semantics are, we need to take a look at how Java methods are differentiated.
In Java, a method is identified by its signature. JLS §8.4.2 specifies that
Two methods have the same signature if they have the same name and argument types.
Important to note is that the return type of a method is not part of a method's signature. Thus if one would write:
public class Foo {
void bar(String baz) {
}
String bar(String baz) {
}
}
Both methods would have the same signature. In Java, this would lead to a compilation error since it is not allowed to have two methods with the same signature in the same class.
The behaviour changes if we take inheritance into the picture:
public class Foo {
void bar(String baz);
}
public class Zoo extends Foo {
#Override
void bar(String baz);
}
In this case, class Zoo overrides method bar(...) of class Foo. Note that the annotation is not responsible for the behaviour and merely a compile-time check to ensure that there is a method void bar(String baz) in at least one parent-class.
The code presented has two method with same name, but different signatures. This is called Overloading in Java. Thus, the method are treated as not "equal". You could rename one of those method and they would not be more or less "equal".
To make things even weirder, if methods are overloaded, the signature for the method to call is made at compile-time. That means that only the static types of parameters can be considered. Let us look at the following code and figure out what the result is:
public class Test {
public static void main(final String... args) {
final String s = "foo";
final Object o = s;
print(s);
print(o);
}
private static void print(final String s) {
System.out.println("Called with String parameter");
}
private static void print(final Object o) {
System.out.println("Called with Object parameter");
}
}
Ideon demo
Now what is the static type of s? It is the type to the left, where s was declared, thus print(final String s) is called and "Called with String parameter" is printed. What is the static type of o? Again, it is the type to the left, where o was declard, and thus print(final Object o) is called and "Called with Object parameter" is printed out. One could argue that in this trivial example, the compiler could figure out that the type of o can only be String, but basing this behaviour on the ability of the compiler to recognize types at compile-time makes it only more confusing.
In java, the whole class is loaded before a method is executed.
This means that the second method is loaded/ready before the first method is executed and the first method is loaded/ready before the second method is executed.
This allows to call a method recursively, and to call a method that will be declared later.
Also, the method is overloaded.
In java, it's possible to create multiple methods with the same name in the same class if the parameters are different. The methods will be treated as different, deoending of the argument that are passed to the method.
In other words, the name alone does not define which method is called but the signature, including the parameters(not the return value)
In the below code I have this line: stream.filter(Data::isEven);
I am using a filter() and a filter() accepts a Predicate interface as parameter. We all know that Predicate has one method with the signature: public boolean test(T t);
which accepts one parameter and returns a boolean.
I am of the understanding that the isEven() method that accepts no parameter is not a valid Predicate because unlike test() method it does not take any parameter, so how come my code is not showing a compile time error?
import java.util.stream.Stream;
public class Main App {
public static void main(String args[]) {
Stream<Data> stream =
Stream.of(new Data(4), new Data(1));
stream.filter(Data::isEven); // should throw compile error but doesn't
}
}
class Data{
int i;
public Data(int i) {
this.i=i;
}
public boolean isEven() {
return i%2==0;
}
}
The thing is that Data::isEven is a method reference equivalent to data -> data.isEven() predicate :
Predicate<Data> predicate = data -> data.isEven();
// is the same as
Predicate<Data> predicate= Data::isEven;
This is described in JLS 15.13 :
The target reference of an instance method (§15.12.4.1) may be provided by the method reference expression using an ExpressionName, a Primary, or super, or it may be provided later when the method is invoked.
....
Evaluation of a method reference expression produces an instance of a functional interface type (§9.8). Method reference evaluation does not cause the execution of the corresponding method; instead, this may occur at a later time when an appropriate method of the functional interface is invoked.
In your case Data::isEven is a reference to instance method isEven of Data object.
Data::isEven is a Predicate.
To call this method you have to pass value, like: myData.isEven().
This is the same as it would be isEven(myData). So the difference is only in syntax (parameter either before a dot, or inside parenthesis), but semantically it is the same.
Therefore isEven is a Predicate<Data> because it accepts Data and returns Boolean.
As others wrote "Data::isEven" or "data -> data.isEven()" is Predicate here. When we invoke test method of this predicate, we pass the instance of data (you have a stream of such instances) there as a parameter.
Assume I have the following functional interface:
public interface TemperatureObserver {
void react(BigDecimal t);
}
and then in another class an already filled-in ArrayList of objects of type TemperatureObserver.
Assuming that temp is a BigDecimal, I can invoke react in a loop using:
observers.forEach(item -> item.react(temp));
My question: can I use a method reference for the code above?
The following does not work:
observers.forEach(TemperatureObserver::react);
The error message is telling me that
forEach in the Arraylist observers is not applicable to the type TemperatureObserver::react
TemperatureObserver does not define a method react(TemperatureObserver)
Fair enough, as forEach expects as an argument a Consumer<? super TemperatureObserver>, and my interface, although functional, does not comply to Consumer because of the different argument of react (a BigDecimal in my case).
So can this be solved, or it is a case in which a lambda does not have a corresponding method reference?
There are three kinds of method references that can be used when a single value is available from the stream:
A parameter-less method of the streamed object.
class Observer {
public void act() {
// code here
}
}
observers.forEach(Observer::act);
observers.forEach(obs -> obs.act()); // equivalent lambda
The streamed object becomes the this object of the method.
A static method with the streamed object as parameter.
class Other {
public static void act(Observer o) {
// code here
}
}
observers.forEach(Other::act);
observers.forEach(obs -> Other.act(obs)); // equivalent lambda
A non-static method with the streamed object as parameter.
class Other {
void act(Observer o);
}
Other other = new Other();
observers.forEach(other::act);
observers.forEach(obs -> other.act(obs)); // equivalent lambda
There is also a constructor reference, but that is not really relevant to this question.
Since you have an external value temp, and you want to use a method reference, you can do the third option:
class Temp {
private final BigDecimal temp;
public Temp(BigDecimal temp) {
this.temp = temp;
}
public void apply(TemperatureObserver observer) {
observer.react(this.temp);
}
}
Temp tempObj = new Temp(temp);
observers.forEach(tempObj::apply);
Take a look at the Method References section in the Java Tutorial. There it says:
There are four kinds of method references:
Reference to a static method: ContainingClass::staticMethodName
Reference to an instance method of a particular object: containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type: ContainingType::methodName
Reference to a constructor: ClassName::new
There it explains that i.e. TemperatureObserver::react would be a method reference of the 3rd type: a reference to an instance method of an arbitrary object of a particular type. In the context of your call to the Stream.forEach method, that method reference would be equivalent to the following lambda expression:
(TemperatureObserver item) -> item.react()
Or just:
item -> item.react()
Which doesn't match your void TemperatureObserver.react(BigDecimal t) method signature.
As you already suspect, there are cases for which you can't find an equivalent method reference for a lambda. Lambdas are way more flexible, though IMHO sometimes they are less readable than method references (but this is a matter of taste, many people think the other way round).
A way to still use a method reference would be with a helper method:
public static <T, U> Consumer<? super T> consumingParam(
BiConsumer<? super T, ? super U> biConsumer,
U param) {
return t -> biConsumer.accept(t, param);
}
Which you could use as follows:
observers.forEach(consumingParam(TemperatureObserver::react, temp));
But, honestly, I prefer to use a lambda.
It does not works, because you iterate over handlers, not over parameters.
For example, this code works:
ArrayList<BigDecimal> temps = new ArrayList<>();
TemperatureObserver observer = new TemperatureObserverImpl();
temps.forEach(observer::react);
OK, the first question in this "series" was this one.
Now, here is another case:
Arrays.asList("hello", "world").stream().forEach(System.out::println);
This compiles, and works...
OK, in the last question, static methods from a class were used.
But now this is different: System.out is a static field of System, yes; it is also a PrintStream, and a PrintStream has a println() method which happens to match the signature of a Consumer in this case, and a Consumer is what forEach() expects.
So I tried this...
public final class Main
{
public static void main(final String... args)
{
Arrays.asList(23, 2389, 19).stream().forEach(new Main()::meh);
}
// Matches the signature of a Consumer<? super Integer>...
public void meh(final Integer ignored)
{
System.out.println("meh");
}
}
And it works!
This is quite a different scope here, since I initiate a new instance and can use a method reference right after this instance is constructed!
So, is a method reference really any method which obeys the signature? What are the limits? Are there any cases where one can build a "#FunctionalInterface compatible" method which cannot be used in a #FunctionalInterface?
The syntax of method references is defined in JLS #15.13. In particular it can be of the form:
Primary :: [TypeArguments] Identifier
Where Primary can be, among other things, a:
ClassInstanceCreationExpression
so yes, your syntax is correct. A few other interesting examples:
this::someInstanceMethod // (...) -> this.someInstanceMethod(...)
"123"::equals // (s) -> "123".equals(s)
(b ? "123" : "456")::equals // where b is a boolean
array[1]::length // (String[] array) -> array[1].length()
String[]::new // i -> new String[i]
a.b()::c // (...) -> a.b().c(...)
By the way, since you mention static methods, it is interesting to note that you can't create a static method reference from an instance:
class Static { static void m() {} }
Static s = new Static();
s.m(); //compiles
someStream.forEach(s::m); //does not compile
someStream.forEach(Static::m); //that's ok
From the State of Lambda
Kinds of method references
There are several different kinds of method references, each with
slightly different syntax:
A static method (ClassName::methName)
An instance method of a particular object (instanceRef::methName)
A super method of a particular object (super::methName)
An instance method of an arbitrary object of a particular type (ClassName::methName)
A class constructor reference (ClassName::new)
An array constructor reference (TypeName[]::new)
Saying this:
something(new Main()::meh);
Is approximately equivalent to saying this:
Main x = new Main();
something(() -> x.meh());
Or this:
final Main x = new Main();
something(new Whatever() {
public void meh(Integer ignored) {
x.meh();
}
}
The new instance is "captured" and used in the new lambda instance which was implicitly created from the method handle.
I'm playing around with Java 8 to find out how functions as first class citizens. I have the following snippet:
package test;
import java.util.*;
import java.util.function.*;
public class Test {
public static void myForEach(List<Integer> list, Function<Integer, Void> myFunction) {
list.forEach(functionToBlock(myFunction));
}
public static void displayInt(Integer i) {
System.out.println(i);
}
public static void main(String[] args) {
List<Integer> theList = new ArrayList<>();
theList.add(1);
theList.add(2);
theList.add(3);
theList.add(4);
theList.add(5);
theList.add(6);
myForEach(theList, Test::displayInt);
}
}
What I'm trying to do is pass method displayInt to method myForEach using a method reference. To compiler produces the following error:
src/test/Test.java:9: error: cannot find symbol
list.forEach(functionToBlock(myFunction));
^
symbol: method functionToBlock(Function<Integer,Void>)
location: class Test
src/test/Test.java:25: error: method myForEach in class Test cannot be applied to given ty
pes;
myForEach(theList, Test::displayInt);
^
required: List<Integer>,Function<Integer,Void>
found: List<Integer>,Test::displayInt
reason: argument mismatch; bad return type in method reference
void cannot be converted to Void
The compiler complains that void cannot be converted to Void. I don't know how to specify the type of the function interface in the signature of myForEach such that the code compiles. I know I could simply change the return type of displayInt to Void and then return null. However, there may be situations where it's not possible to alter the method I want to pass somewhere else. Is there an easy way to reuse displayInt as it is?
You are trying to use the wrong interface type. The type Function is not appropriate in this case because it receives a parameter and has a return value. Instead you should use Consumer (formerly known as Block)
The Function type is declared as
interface Function<T,R> {
R apply(T t);
}
However, the Consumer type is compatible with that you are looking for:
interface Consumer<T> {
void accept(T t);
}
As such, Consumer is compatible with methods that receive a T and return nothing (void). And this is what you want.
For instance, if I wanted to display all element in a list I could simply create a consumer for that with a lambda expression:
List<String> allJedi = asList("Luke","Obiwan","Quigon");
allJedi.forEach( jedi -> System.out.println(jedi) );
You can see above that in this case, the lambda expression receives a parameter and has no return value.
Now, if I wanted to use a method reference instead of a lambda expression to create a consume of this type, then I need a method that receives a String and returns void, right?.
I could use different types of method references, but in this case let's take advantage of an object method reference by using the println method in the System.out object, like this:
Consumer<String> block = System.out::println
Or I could simply do
allJedi.forEach(System.out::println);
The println method is appropriate because it receives a value and has a return type void, just like the accept method in Consumer.
So, in your code, you need to change your method signature to somewhat like:
public static void myForEach(List<Integer> list, Consumer<Integer> myBlock) {
list.forEach(myBlock);
}
And then you should be able to create a consumer, using a static method reference, in your case by doing:
myForEach(theList, Test::displayInt);
Ultimately, you could even get rid of your myForEach method altogether and simply do:
theList.forEach(Test::displayInt);
About Functions as First Class Citizens
All been said, the truth is that Java 8 will not have functions as first-class citizens since a structural function type will not be added to the language. Java will simply offer an alternative way to create implementations of functional interfaces out of lambda expressions and method references. Ultimately lambda expressions and method references will be bound to object references, therefore all we have is objects as first-class citizens. The important thing is the functionality is there since we can pass objects as parameters, bound them to variable references and return them as values from other methods, then they pretty much serve a similar purpose.
When you need to accept a function as argument which takes no arguments and returns no result (void), in my opinion it is still best to have something like
public interface Thunk { void apply(); }
somewhere in your code. In my functional programming courses the word 'thunk' was used to describe such functions. Why it isn't in java.util.function is beyond my comprehension.
In other cases I find that even when java.util.function does have something that matches the signature I want - it still doesn't always feel right when the naming of the interface doesn't match the use of the function in my code. I guess it's a similar point that is made elsewhere here regarding 'Runnable' - which is a term associated with the Thread class - so while it may have he signature I need, it is still likely to confuse the reader.
Set return type to Void instead of void and return null
// Modify existing method
public static Void displayInt(Integer i) {
System.out.println(i);
return null;
}
OR
// Or use Lambda
myForEach(theList, i -> {System.out.println(i);return null;});