Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reference to an instance method of a particular object

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
    }
}
like image 358
Andrey Avatar asked Aug 20 '14 16:08

Andrey


1 Answers

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());
like image 105
Jon Skeet Avatar answered Dec 08 '22 11:12

Jon Skeet