Reference to the instance method of a specific object

In the following code, it works when passing a reference variable of a method with a class name, but an error occurs when passing a reference variable with a user object.

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 } } 
+6
source share
4 answers

userList.forEach expects a Consumer<? extends User> Consumer<? extends User> - in other words, a method that accepts a User link and does something with it.

It could be:

  • A static method that takes a User parameter, in which case the parameter will be filled with the corresponding element in the list at each iteration:

     staticMethod(userFromList) 
  • An instance method (of any class) that accepts a User parameter, provided with a specific instance to enable it - again, the parameter will be filled with the corresponding element:

     target.instanceMethod(userFromList) 
  • An instance method on User without parameters provided without a specific instance, in this case, the purpose of the method call will be the corresponding element in the list at each iteration:

     userFromList.instanceMethod() 

Since you tried to specify the target, and the method does not have any parameters, the forEach method has nothing to do with each element - it cannot pass it as an argument, because the method doesn’t have any parameters, and it cannot use it as the target of the method, as you have already specified it.

Your working code shows a third example. Here are two other methods that let you 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 in the same User three times, ignoring User in the list, you can use:

 userList.forEach(ignored -> u1.printName()); 
+13
source

it

 u1::printName 

is the method reference for calling the object referenced by ui . The compiler does not know how to interpret the argument that it should pass to the Consumer lambda. It is best to assume that it should be referred to as

 u1.printName(arg); 

but such a method does not exist.

+2
source

Based on http://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html , we know that method references are similar to the 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 

because it means that in the accept Consumer method that this lambdas "implements", you will call printName() for each User passed to this method.

But in case

 userList.forEach(u1::printName); // compile error 

this code represents the following lambda

 userList.forEach((User u) -> u1.printName(u)); // compile error // ^^^ // method doesn't accept User argument 

so you are trying to call printName from the instance contained in the u1 link and pass each User from the list as an argument to the method, but as you can see

 public void printName() 

cannot accept a User instance as an argument, so you see a compile-time error.

+2
source

Method reference

 u1::printName 

essentially equivalent to this lambda:

 () -> u1.printName() 

This is because printName has no arguments. If you have a printNameWithWidth(int width) method, then u1::printNameWithWidth will be equivalent

 (int width) -> u1.printNameWithWidth(width) 

But the fact is that none of the arguments has a User value, since you already told him which User use (i.e. u1 ). forEach does not like. An argument requires a lambda (or equivalent) with User (or any other type of element).

It:

 User::printName 

equivalently

 (User x) -> x.printName() 

therefore it works.

+1
source

Source: https://habr.com/ru/post/974110/


All Articles