java 使用函数式接口的疑问

java 使用函数式接口的疑问
为什么第一个第一次调用functionTest方法时提示Non-static method cannot be referenced from a static context
为什么不能这么使用,第二次调用functionTest方法为什么没有报错,有没有大概详细解释一下,这一块不太懂。。
以下是全部代码

public class FunctionTest1 {
    public static void main(String[] args) {
        List<UserInfo> userInfoList = getUserList();
        userInfoList.forEach(obj -> {
            //提示Non-static method cannot be referenced from a static context
            functionTest(obj, UserInfo::setNameDescTest);
            //不报错
            functionTest(obj, obj::setNameDescTest);
            //不报错
            functionTest1(obj, UserInfo::getNameDesc);
        });
    }
    
    public static void functionTest(UserInfo userInfo, Function<String, Boolean> function) {
        Boolean apply = function.apply(userInfo.toString());
        System.out.println(apply);
    }
    public static void functionTest1(UserInfo userInfo, Function<UserInfo, String> function) {
        String apply = function.apply(userInfo);
        System.out.println(apply);
    }
    
    public static List<UserInfo> getUserList() {
        List<UserInfo> userInfoList = new ArrayList<>();
        UserInfo userInfo = new UserInfo();
        UserInfo userInfo1 = new UserInfo();
        userInfoList.add(userInfo);
        userInfoList.add(userInfo1);
        return  userInfoList;
    }
}



public class UserInfo {
    private int age;
    private String name;
    private Double money;
    private String nameDesc;

    public UserInfo() {
    }

    public UserInfo(int age, String name, Double money) {
        this.age = age;
        this.name = name;
        this.money = money;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", money=" + money +
                ", nameDesc='" + nameDesc + '\'' +
                '}';
    }

    public  int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }
    public static void testStatic() {
        System.out.println("testStatic....");
    }

    public String getNameDesc() {
        this.name = "545tr";
        return nameDesc;
    }
    public boolean setNameDescTest(String nameDesc) {
        //this.nameDesc = nameDesc;
        System.out.println(nameDesc);
        return true;
    }

    public void setNameDesc(String nameDesc) {
        this.nameDesc = nameDesc;
    }
}

“Devil组”引证GPT后的撰写:

  • 首先,函数式接口是指只有一个抽象方法的接口,可以用 lambda 表达式或方法引用来实现该接口。在 Java 8 中,引入了许多新的函数式接口来支持 Lambda 表达式和方法引用。
  • 对于你的第一个问题,当你在 main 方法中调用 functionTest 时,你传递了一个实例方法引用 UserInfo::setNameDescTest。由于该方法不是静态方法,所以不能从静态上下文中引用它,这就是为什么你得到了“Non-static method cannot be referenced from a static context”错误。
  • 但是,当你在第二次调用 functionTest 时,你传递了一个对 obj 实例的方法引用 obj::setNameDescTest。此时,实例方法 setNameDescTest 能够正确地从实例上下文中引用,因为它是针对实例对象调用的。
  • 对于你的第二个问题,你调用 functionTest1 方法时使用了一个函数式接口 Function<UserInfo, String>,该接口的方法签名为 apply(UserInfo),因此你可以传递一个接受 UserInfo 对象并返回 String 值的方法引用,如 UserInfo::getNameDesc。在这种情况下,因为 getNameDesc 是实例方法,它能够正确地从实例上下文中引用。

该回答引用ChatGPT

在Java中,如果要使用一个非静态方法,必须先创建一个该类的实例,然后通过该实例来调用方法。静态方法可以通过类名直接调用,无需创建实例。

在你的代码中,当你尝试通过 UserInfo::setNameDescTest 方法引用来调用 functionTest 方法时,Java会将该方法引用解析为非静态方法引用,即需要通过实例调用 setNameDescTest 方法。但是,你在 main 方法中的 functionTest 方法是静态的,无法通过实例调用非静态方法。因此编译器会提示错误。

而在第二次调用 functionTest 方法时,你传递了 obj::setNameDescTest,这是一个方法引用表达式,将 setNameDescTest 方法绑定到 obj 实例上。这个方法引用会在 functionTest 方法中被解析为非静态方法引用,并且通过该实例来调用 setNameDescTest 方法。

如果你想使用 UserInfo::setNameDescTest 方法引用,你需要将 functionTest 方法设置为非静态方法,或者先创建一个 UserInfo 类的实例,然后通过该实例来调用 functionTest 方法。

参考GPT和自己的思路,在Java中,非静态方法需要实例化对象后才能调用,而静态方法可以通过类名直接调用,因此当你在静态方法中调用非静态方法时会提示"Non-static method cannot be referenced from a static context"。在你的代码中,第一个调用是通过类名调用了UserInfo类的非静态方法setNameDescTest,因此会提示该错误。

而在第二个调用中,你通过obj::setNameDescTest引用了一个非静态方法,并将其作为参数传递给了functionTest方法,此时你已经实例化了UserInfo对象,因此不会提示该错误。

如果你想要在静态方法中调用非静态方法,你需要先实例化一个对象,然后再通过该对象调用非静态方法,例如:

public static void main(String[] args) {
    List<UserInfo> userInfoList = getUserList();
    userInfoList.forEach(obj -> {
        UserInfo user = new UserInfo();
        functionTest(user, obj::setNameDescTest);
    });
}

这样就可以避免上述错误了。

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
问题1:为什么第一个第一次调用functionTest方法时提示Non-static method cannot be referenced from a static context,为什么不能这么使用?

在第一个第一次调用 functionTest 方法中使用的是UserInfo 类型的实例 obj 来引用非静态方法setNameDescTest

functionTest(obj, UserInfo::setNameDescTest);

而在Java中,只有类的实例才能够调用非静态方法。而在静态方法中,我们只能使用静态成员变量和静态方法。

因此,在使用 functionTest 方法时,我们需要采用以下两种方法来传入 setNameDescTest 方法:

  • 使用类的实例引用方法,如下所示:
UserInfo userInfo = new UserInfo();
functionTest(userInfo, userInfo::setNameDescTest);
  • setNameDescTest方法改成静态方法,如下所示:
public static boolean setNameDescTestStatic(UserInfo userInfo, String nameDesc) {
        System.out.println(nameDesc);
        return true;
}

问题2:第二次调用functionTest方法为什么没有报错?

在第二次调用 functionTest 方法中,使用的是 obj 实例来引用非静态方法setNameDescTest。第一次调用时出现错误是因为在静态方法中我们只能访问静态方法和静态变量。

因此,当我们将 obj 实例传入 functionTest 方法时,方法内部调用到的是 setNameDescTest 实例方法而不是静态方法。而第二次调用中出现的obj::setNameDescTest其实是编译器进行的语法糖转化,即将 obj 实例转换成了 UserInfo 类的实例,所以没有报错。

以下是修改后的代码:

public class FunctionTest1 {
    public static void main(String[] args) {
        List<UserInfo> userInfoList = getUserList();
        userInfoList.forEach(obj -> {
            //第一种方法:使用类的实例引用方法
            UserInfo userInfo = new UserInfo();
            functionTest(userInfo, userInfo::setNameDescTest);
            //第二种方法:将 setNameDescTest 方法改成静态方法
            functionTest(obj, UserInfo::setNameDescTestStatic);
            //不报错
            functionTest(obj, obj::setNameDescTest);
            //不报错
            functionTest1(obj, UserInfo::getNameDesc);
        });
    }
    
    public static void functionTest(UserInfo userInfo, BiFunction<UserInfo, String, Boolean> function) {
        Boolean apply = function.apply(userInfo, userInfo.toString());
        System.out.println(apply);
    }
    public static void functionTest1(UserInfo userInfo, Function<UserInfo, String> function) {
        String apply = function.apply(userInfo);
        System.out.println(apply);
    }
    
    public static List<UserInfo> getUserList() {
        List<UserInfo> userInfoList = new ArrayList<>();
        UserInfo userInfo = new UserInfo();
        UserInfo userInfo1 = new UserInfo();
        userInfoList.add(userInfo);
        userInfoList.add(userInfo1);
        return  userInfoList;
    }


    public static boolean setNameDescTestStatic(UserInfo userInfo, String nameDesc) {
        System.out.println(nameDesc);
        return true;
    }
}

如果我的回答解决了您的问题,请采纳!

参考GPT的内容和自己的思路,在Java中,非静态方法是实例方法,需要使用对象来调用。而静态方法是类方法,可以直接使用类名来调用。在你的代码中,你在静态方法main中尝试调用一个非静态方法UserInfo::setNameDescTest,但是该方法需要使用对象来调用。因此编译器会提示Non-static method cannot be referenced from a static context,表示非静态方法不能从静态上下文中引用。

第二次调用functionTest方法时,你使用obj::setNameDescTest作为参数,其中obj是一个UserInfo对象。这样做是可以的,因为你使用对象来调用该方法。在这种情况下,编译器会将obj::setNameDescTest转换为一个函数对象,该对象将在调用时自动使用obj作为调用者。

对于functionTest1方法,你将Function<UserInfo, String>作为参数。因为该函数接口的参数和返回值都是UserInfo类型,所以你可以将一个UserInfo对象作为参数传递给该方法。在这种情况下,编译器会将UserInfo::getNameDesc转换为一个函数对象,该对象将在调用时自动使用userInfo作为调用者。

以下是修改后的代码,使得functionTest方法可以使用方法引用来调用UserInfo类中的非静态方法:

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

public class FunctionTest1 {
    public static void main(String[] args) {
        List<UserInfo> userInfoList = getUserList();
        userInfoList.forEach(obj -> {
            functionTest(obj, UserInfo::setNameDescTest);
            functionTest1(obj, UserInfo::getNameDesc);
        });
    }

    public static void functionTest(UserInfo userInfo, Function<UserInfo, Boolean> function) {
        Boolean apply = function.apply(userInfo);
        System.out.println(apply);
    }

    public static void functionTest1(UserInfo userInfo, Function<UserInfo, String> function) {
        String apply = function.apply(userInfo);
        System.out.println(apply);
    }

    public static List<UserInfo> getUserList() {
        List<UserInfo> userInfoList = new ArrayList<>();
        UserInfo userInfo = new UserInfo();
        UserInfo userInfo1 = new UserInfo();
        userInfoList.add(userInfo);
        userInfoList.add(userInfo1);
        return userInfoList;
    }
}

class UserInfo {
    private int age;
    private String name;
    private Double money;
    private String nameDesc;

    public UserInfo() {
    }

    public UserInfo(int age, String name, Double money) {
        this.age = age;
        this.name = name;
        this.money = money;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", money=" + money +
                ", nameDesc='" + nameDesc + '\'' +
                '}';
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    public static void testStatic() {
        System.out.println("testStatic....");
    }

    public String getNameDesc() {
        this.name = "545tr";
        return nameDesc;
    }

    public boolean setNameDescTest() {
        System.out.println(this.toString());
        return true;
    }

    public void setNameDesc(String nameDesc) {
        this.nameDesc = nameDesc;
    }
}


在修改后的代码中,我将functionTest方法的第二个参数的类型改为Function<UserInfo, Boolean>,使得该方法可以使用方法引用来调用UserInfo类中的非静态方法setNameDescTest。我还修改了UserInfo类中的setNameDescTest方法,将其参数列表删除,使得该方法可以在调用时不需要传递任何参数。

回答不易,还请采纳!!!

第一次调用functionTest方法时,使用的是类名调用,即静态方法,但UserInfo::setNameDescTest是非静态方法的引用,不能从静态上下文中引用非静态方法。因此会提示"Non-static method cannot be referenced from a static context",表示无法从静态上下文中引用非静态方法。

第二次调用functionTest方法时,使用的是对象调用,即动态方法,obj::setNameDescTest是动态方法的引用,可以从对象上下文中引用非静态方法,因此不会报错。

functionTest1方法中,使用的是对象调用,即动态方法,因此可以使用非静态方法引用和对象方法引用,均不会报错。

在Java中,非静态方法需要先创建类的对象(即实例化)才能被调用。而在第一个调用functionTest方法时,UserInfo::setNameDescTest表示的是一个实例方法引用,需要先创建UserInfo对象才能调用该方法,因此会提示“Non-static method cannot be referenced from a static context”。

而在第二次调用functionTest方法时,使用的是obj::setNameDescTest,obj是UserInfo类型的实例,因此可以调用实例方法而不会提示错误。

至于functionTest1方法,使用的是Function<UserInfo, String>类型的参数,它表示的是一个接受一个UserInfo类型参数并返回String类型的函数,而UserInfo::getNameDesc也是一个实例方法引用,因此在第三个调用functionTest1方法时,不会提示错误。

该回答结合了我自己的回答与ChatGPT的回答

在第一个调用 functionTest 方法时,您正在尝试从一个静态方法中引用一个非静态方法 UserInfo::setNameDescTest。由于 main 方法是静态方法,它没有隐含的 this 指针,因此无法引用非静态方法。

在第二个调用 functionTest 方法时,您正在使用实例方法引用 obj::setNameDescTest,这将生成一个函数接口实例,该实例将在 obj 上调用 setNameDescTest 方法。因此,这里不会出现上述的静态方法和非静态方法的问题。

另外需要注意的是,Function 是一个函数式接口,它是一个只有一个抽象方法的接口。在这里,您可以使用 lambda 表达式或方法引用来实现 apply 方法,并将其传递给 functionTest 方法。这是函数式编程的一种方式,它使您可以将行为作为参数传递给方法,并将方法的行为进行抽象化。

非常感谢各位的解答,辛苦各位了
在下面的链接的第六点也找到了找到了自己比较想知道的答案

https://blog.csdn.net/smile4548656/article/details/124425501#_1