Java 对特定对象的实例方法的引用

Java 对特定对象的实例方法的引用,java,lambda,java-8,method-reference,Java,Lambda,Java 8,Method Reference,在下面的代码中,它在用类名传递方法引用变量时工作,但在用用户对象传递引用变量时出错 public class User { private String name; public User(String name) { this.name = name; } public void printName() { System.out.println(name); } } public class Main {

在下面的代码中,它在用类名传递方法引用变量时工作,但在用用户对象传递引用变量时出错

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
    }
}
公共类用户{
私有字符串名称;
公共用户(字符串名称){
this.name=名称;
}
public void printName(){
System.out.println(名称);
}    
}
公共班机{
公共静态void main(字符串[]args){
用户u1=新用户(“AAA”);
用户u2=新用户(“BBB”);
用户u3=新用户(“ZZZ”);
List userList=Arrays.asList(u1、u2、u3);
userList.forEach(User::printName);//有效
userList.forEach(u1::printName);//编译错误
}
}
这个

是要在
ui
引用的对象上调用的方法引用。编译器不知道如何解释它应该传递给
使用者的参数。它最好的猜测是它应该作为

u1.printName(arg);

但是这样的方法并不存在。

userList.forEach
希望有一个
消费者,因为我们知道方法引用类似于下面的lambda

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)
所以你的代码

userList.forEach(User::printName); // works
可以重写为

userList.forEach((User u) -> u.printName()); // works
这是可以的,因为这意味着在这个lambdas“实现”的
accept
消费者
方法中,您将对传递给此方法的每个
用户调用
printName()

但是万一

userList.forEach(u1::printName); // compile error
此代码表示以下lambda

userList.forEach((User u) -> u1.printName(u)); // compile error
//                                       ^^^   // method doesn't accept User argument
因此,您正试图从
u1
引用所持有的实例中调用
printName
,并将列表中的每个
User
作为此方法参数传递,但如您所见

public void printName() 
无法接受
User
的实例作为其参数,这就是您看到编译时错误的原因。

方法引用

u1::printName
本质上等同于此λ:

() -> u1.printName()
这是因为
printName
没有任何参数。如果您有一个
printNameWithWidth(int-width)
方法,那么
u1::printNameWithWidth
将等效于

(int width) -> u1.printNameWithWidth(width)
(User x) -> x.printName()
但关键是,在这两种情况下,
用户
都不是参数之一,因为您已经告诉它要使用哪个
用户(即
u1
forEach
不喜欢这样。它需要一个lambda(或等效值),并带有
用户
(或任何其他元素类型)作为参数

这:

相当于

(int width) -> u1.printNameWithWidth(width)
(User x) -> x.printName()
这就是它起作用的原因

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);

}
工作正常,因为forEach方法在内部调用使用者接口的方法accept(arg1),而用户定义的方法printName没有args。因此,根据我的上述解释,它是正确的,编译器不会抱怨

给出编译错误,因为您在引用实例方法printName时正在使用对象u1。所以编译器期望printName方法的参数数量和使用者接口的accept方法中的参数数量相同。因此,它将尝试从类User中查找printName(User param1)。由于没有找到,编译器也会抱怨同样的问题


我希望这对你们有帮助。如果我遗漏了什么,或者说了什么错话,也要告诉我

我认为这是在Java中使用方法引用的关键点。我学习它真的很困难。这是一个很好的案例来源。此外,以下摘自的图片有助于记忆


传入
u1::printName
,您希望它意味着什么?你希望它打印三次“AAA”吗?如果没有,您希望
forEach
如何为
printName
方法提供调用
printName
的正确用户?是的,我希望打印三次“AAA”。这只是一个测试,我正在学习好的-那么你希望
forEach
对它迭代的每个用户做什么呢?(详见我的答案)事实上,我对这些值不感兴趣​​这将在如何使用实例化对象而不是类名的方法引用中产生。你的回答很完美,现在我明白了。谢谢
u1::printName
() -> u1.printName()
(int width) -> u1.printNameWithWidth(width)
User::printName
(User x) -> x.printName()
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);

}
    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
userList.forEach(User::printName);
userList.forEach(u1::printName);