In the following code, it works when passing the method reference variable with the class name, but when passing the reference variable with a user object there is an error.
public class User {
private String name;
public User(String name) {
this.name = name;
}
public void printName() {
System.out.println(name);
}
}
public class Main {
public static void main(String[] args) {
User u1 = new User("AAA");
User u2 = new User("BBB");
User u3 = new User("ZZZ");
List<User> userList = Arrays.asList(u1, u2, u3);
userList.forEach(User::printName); // works
userList.forEach(u1::printName); // compile error
}
}
userList.forEach is expecting a Consumer<? extends User> - in other words, a method which accept a User reference and do something with it.
That could be:
A static method accepting a User parameter, in which case the parameter will be populated with the relevant element in the list on each iteration:
staticMethod(userFromList)
An instance method (of any class) accepting a User parameter, provided with a specific instance to call it on - again, the parameter will be populated with the relevant element:
target.instanceMethod(userFromList)
An instance method on User with no parameters, provided without a specific instance, which case the target of the method call will be the relevant element in the list on each iteration:
userFromList.instanceMethod()
Because you've tried to specify a target and the method doesn't have any parameters, the forEach method has nothing it can do with each element - it can't pass it as an argument because the method doesn't have any parameters, and it can't use it as the method target because you've already specified one.
Your working code shows the third example. Here are two other methods to allow you to demonstrate the first two:
public class UserPrinter {
private final String name;
public UserPrinter(String name) {
this.name;
}
public static void staticPrintUser(User user) {
// Assuming you add a User.getName() method
System.out.println("staticPrintUser: " + user.getName());
}
public void instancePrintUser(User user) {
System.out.println("instancePrintUser (instance " + name + "): "
+ user.getName());
}
}
Then:
userList.forEach(UserPrinter::staticPrintUser); // equivalent to
//userList.forEach(p -> UserPrinter.staticPrintUser(p));
UserPrinter printer = new UserPrinter("my printer");
userList.forEach(printer::instancePrintUser); // equivalent to
//userList.forEach(p -> printer.instancePrintUser(p));
If you really want to call printUser on the same User three times, ignoring the User in the list, you could use:
userList.forEach(ignored -> u1.printName());
Based on http://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html we know that method references are similar to following lambdas
method reference ==> lambda
------------------------------------------------------------------------------------
object::method ==> (Foo f, Bar b, Baz z) -> object.method(f,b,z)
SomeClass::staticMethod ==> (Foo f, Bar b, Baz z) -> SomeClass.staticMethod(f,b,z)
SomeClass::instanceMethod ==> (Foo f, Bar b, Baz z) -> f.instanceMethod(b,z)
SomeClass::new ==> (Foo f, Bar b, Baz z) -> new SomeClass(f,b,z)
So your code
userList.forEach(User::printName); // works
can be rewritten as
userList.forEach((User u) -> u.printName()); // works
which is OK because it means that in accept method of Consumer which this lambdas "implements" you will invoke printName() on each User passed to this method.
But in case of
userList.forEach(u1::printName); // compile error
this code represents following lambda
userList.forEach((User u) -> u1.printName(u)); // compile error
// ^^^ // method doesn't accept User argument
so you are trying to invoke printName from instance held by u1 reference and pass each User from list as this method argument, but as you see
public void printName()
can't accept instance of User as its argument, which is why you are seeing compile time error.
This
u1::printName
is a method reference to be invoked on the object referenced by ui. The compiler doesn't know how to interpret the argument it should pass to the Consumer lambda. Its best guess is that it should be passed as
u1.printName(arg);
but such a method doesn't exist.
The method reference
u1::printName
is essentially equivalent to this lambda:
() -> u1.printName()
This is because printName doesn't have any arguments. If you had a printNameWithWidth(int width) method, then u1::printNameWithWidth would be equivalent to
(int width) -> u1.printNameWithWidth(width)
But the point is that in neither case is a User one of the arguments, since you've already told it which User to use (i.e. u1). forEach doesn't like that. It needs a lambda (or the equivalent) with a User (or whatever other element type) as an argument.
This:
User::printName
is equivalent to
(User x) -> x.printName()
which is why it works.
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
public class Testing {
public static void main(String[] args) {
List<B> list = new ArrayList<B>();
B b1=new B(); b1.setName("Durgesh");
B b2=new B(); b2.setName("Val");
list.add(b1);list.add(b2);
MyInterface<B> my1 = B :: printName;
my1.dummyDisplay(b1,b2);
MyInterface<B> my2 = (a,b) -> a.printName(b);
my2.dummyDisplay(b1,b2);
// MyInterface<B> my3 = b1::printName; //compilation error
}
}
class B{
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void printName(B b) {
System.out.println(this.name + b.name);
}
}
#FunctionalInterface
interface MyInterface <T> {
public void dummyDisplay(T s, T t);
}
Below lines of code works fine even though printName method of class B accepts only 1 parameter while method dummyDisplay accepts 2 parameters. It is because when we call dummyDisplay ( of functional interface ) method with 2 arguments, compiler uses one argument to invoke printName method and another argument is passed as an argument to printName method. That means (arg1).printName(arg2). Notice the usage of "this" keyword in the method printName.So always remember that in such kinds of method references, number of paramters of the method to be called(printName) should always be 1 less than the number of parameters used in method(dummyDisplay) of functional interface. Such kind of method reference is very frequently used when dealing with POJO classes where we use getters(no arg) of the classes given the functional interface(say Function/Consumer - having methods with 1 parameter).
MyInterface<B> my1 = B :: printName;
my1.dummyDisplay(b1,b2);
I hope you understand this concept.
Now coming to below lines of code. This code is just a replacement of method reference which we discussed just now. Here, since method declared in functional interface has 2 parameters, so we have to and had to use 2 arguments in lambda expression (in this case its a and b). Then a.printName(b) will be written as the definition of interface method (dummyDisplay). Its straight forward. Lambda expression can be used any where provided Functional interfaces(off course).
MyInterface<B> my2 = (a,b) -> a.printName(b);
my2.dummyDisplay(b1,b2);
Now coming to last piece of code. We get compilation error because compiler expects exactly the same number of parameters in printName method of class B which are there in the method of functional interface. Normally this kind of method reference is used just to call any random method of any class which accepts some parameters and does some processing on the accepted data. eg. say add/multiply/divide methods present in the class Calculate or the compare method of Comparator functional interface. In all these cases, method definition does not use "this" keyword. It simply accepts some parameters and performs some task on them. I hope you guys got something out of it.
MyInterface<B> my3 = b1::printName; //compilation error
Having said this, Now lets come to your question,
userList.forEach(User::printName);
works fine because, forEach method internally calls a method accept(arg1) of Consumer interface and your user-defined method printName has no args. So as per my above explanation, it is correct and the compiler does not complaint.
and
userList.forEach(u1::printName);
gives compilation error, because you are using object u1 while referencing instance method printName . So compiler expects same number of parameters of printName method as that there are in accept method of Consumer interface. So it will try to find printName(User param1) from your class User. And since it is not found, compiler complaints about the same.
I hope this helps you guys. Also let me know if I have missed anything or if I have said some thing wrong.
I think it's the key point of usage of method references in Java. I had really difficulty in learning it. #Pshemo's answer is really nice source for this case. Besides, the following image excerpted from Java 8 in Action is helpful to remember.
Related
In the following program, I have one single method in class A overloaded 3 times and then in subclass B, all 3 overloaded methods are overridden.
obj3 is an object with reference type A(superclass) and object type B(subclass) and it calls the method from B on execution, which is expected behavior.
Since overloading and overriding both exist in this code, does that mean that it performed static binding at compile time (to the matching method in class A) and then dynamic binding at run time (to method in class B). Can they both occur together?
My assumption is that this is a classic case of dynamic binding as I believed "binding" is meant to be a permanent action, but a peer suggests that it is both together(static first, then dynamic).
class A{
public void method(Integer n){
System.out.println("Integer: "+n);
}
public void method(String s){
System.out.println("String: "+s);
}
public void method(String s, Integer n){
System.out.println("String: "+s+" Integer: "+n);
}
}
class B extends A{
public void method(Integer n){
System.out.println("Integer(from B): "+n);
}
public void method(String s){
System.out.println("String(from B): "+s);
}
public void method(String s, Integer n){
System.out.println("String(from B): "+s+" Integer(from B): "+n);
}
}
public class Test{
public static void main(String[] args){
A obj1 = new A();
B obj2 = new B();
A obj3 = new B();
System.out.println("Integer form of method");
// Integer form of method
System.out.println("Ref A Obj A");
// Ref A Obj A
obj1.method(1);
// Integer: 1
System.out.println("Ref B Obj B");
// Ref B Obj B
obj2.method(2);
// Integer(from B): 2
System.out.println("Ref A Obj B");
// Ref A Obj B
obj3.method(3);
// Integer(from B): 3
}
}
Since overloading and overriding both exist in this code, does that mean that it performed static binding at compile time (to the matching method in class A) and then dynamic binding at run time (to method in class B)
Right. The compiler chose the matching signature, and this is static, based on the type of the variable (A in this case).
At runtime, Java finds the implementation of the signature selected by the compiler. This is dynamic, based on the runtime class of obj3 (B, in this case).
You right. Compiler is statically choose between overloads in class A and put that information into .class file in form of method FQN.
Then runtime dynamically choose between implementation of that method.
One more attempt to make it clear:
Overloading
Overloading means that a single class has multiple methods with different parameter types (aka. signatures), and you just happened to give them the same name. Your program will work the very same if you change to individual names for the methods, e.g. methodI(Integer n), methodS(String s), and methodSI(String s, Integer n).
Or, you can imagine the compiler to internally always append such a types list to the method name.
Overloading is resolved by the compiler, based on the compile-time types of the parameter expressions.
E.g. if you write
Object par = "Test";
a.method(par);
you get a compiler error. Even though we all see it's a String that you are passing into the method, the compiler only sees an Object, and finds no matching method. Only if you were to introduce an additional method(Object o), the compiler would choose that one. And runtime would call that method and not the String version!
Overriding
Overriding means that at runtime the JVM calls the method implementation depending on the runtime class of the "object before the dot".
And in this case, "method" is to be read as the overloaded method version that the compiler found to match the parameter list. So, runtime already knows whether methodI(), methodS(), or methodSI() is meant, and only decides from which class to take the implementation.
Personal opinion
Allowing multiple methods to share the same name, but differ in parameter lists (aka overloading), produces too much confusion for its benefit.
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.
I have a method that selects between the arguments of an array and returns a specific one. For instance, here is that method:
private <T> T selectOnType(T[] selection, T defaultOp){
switch(this.type){
case Resources.TEXT:
return selection[Resources.TEXT];
case Resources.LISTEN:
return selection[Resources.LISTEN];
default:
return defaultOp;
}
}
How can I construct an array full of method references (i.e. function pointers) in order to be able to pass that array into this method above?
I tried doing such things as:
java.util.function.Function<Void, Void>[] array = {ClassA::method1, ClassA::method2};
(where method1 and method1 take no arguments and return void)
But that throws a compiler error saying:
incompatible types: invalid method reference but expected no arguments. found: java.lang.Void reason: actual and formal argument lists differ in length
I have been playing around with lambdas such as:
() -> ClassA.method1()
But I haven't been able to get it to work. Does anyone know what I am doing wrong and know a solution to this problem?
EDIT:
I have seen this on Stack Overflow, but this is for C# and I haven't figured out how to mimic it in Java.
Example:
Let's say I have a Word class:
public class Word{
private final String text;
private int listenCorrect = 0, textCorrect = 0;
public Word(final String test){
this.text = text;
}
public void incListenCorrect(){
listenCorrect++;
}
public void incTextCorrect(){
textCorrect--;
}
}
And finally I have a Main class. Inside the action method (in the Main class) I want to have an array with these two methods in it in order to select between them if the type (shown below) is either listen or text:
public class Main{
int type = 0;
public void action(){
Word word = new Word("Hello");
// 'Functions' is used to represent something I tried above (just for demonstration)
Function[] array = {word::incListenCorrect, word::incTextCorrect};
Function picked = selectOnType(array, word::incTextCorrect);
picked.call();
}
/*
* Resources is another class that contains the following values:
* public static final int TEXT = 0;
* public static final int LISTEN = 1;
*/
private <T> T selectOnType(T[] selection, T defaultOp){
switch(this.type){
case Resources.TEXT:
return selection[Resources.TEXT];
case Resources.LISTEN:
return selection[Resources.LISTEN];
default:
return defaultOp;
}
}
}
A Function is a method that takes one argument and returns a result. You're using methods that take no arguments and do not return results. You can't use Function for this (using Void isn't a way to get around this), but the java.util.function package contains a number of classes for different common combinations (methods that take no arguments but return a result, methods that take one or two arguments and don't return a result, methods that take primitive arguments or return primitive results that won't work in a Function because the types aren't class types, etc.).
There isn't a class in java.util.function for a functional interface with no arguments and no result, but Runnable can be used for that.
You need to make sure you use the correct interface.
Note: I was assuming method1 and method2 are static methods, so that they don't take any arguments, even a hidden "instance" argument that instance methods take. If they're instance methods, then things have to be done differently.
Now that you've clarified that they're instance methods, things are different--but it depends on how you get the method. If you say
Word::incListenCorrect
since you're using the class name, you need to provide the instance as an argument. Therefore, Word::incListenCorrect returns a functional interface for a method that takes one argument, such as Consumer<Word>, and you have to pass the Word as the argument when you call the method with .accept(). But:
word::incListenCorrect
is very different. Now, the word instance becomes "baked into" the method reference, so it doesn't need to be passed as an argument. In this case, therefore, you'll still need the interface that takes no arguments and does not return a value, which is Runnable. When you say
Runnable r = word::incListenCorrect;
r.run();
where r is a Runnable, it will automatically use word as the instance for the instance method, since word became part of r when you assigned the method reference to it.
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;});