静态方法和实例方法的方法参考

问题描述

对于 Java 中的实例方法,我无法掌握方法引用的概念

例如在下面的示例中,编译器在列表行中给出错误

我看过 String::toupperCase 的例子。

我对这一点感到困惑 (1) String 是一个类, toupperCase 是实例方法。 Java 允许 String::toupperCase (2) 为什么在我的情况下不允许:- AppTest::makeUppercase

package mja;

import java.util.function.Function;
public class AppTest {

    public String makeUppercase(String source){
        return source.toupperCase();
    }
    
    public void printFormattedString(String string,Function<String,String> formatter){
        System.out.println(formatter.apply(string));
    }
    
    public static void main(String[] args) {
        AppTest appTest = new Apptest();
        String source = "Hello World!";
        
        // Below statement compiled successfully
        appTest.printFormattedString(source,appTest::makeUppercase);

        // Getting error that non-static method can't be referenced from static context
        appTest.printFormattedString(source,AppTest::makeUppercase);
    }
}

解决方法

为什么不允许 AppTest::makeUppercase

简短的回答是 AppTest::makeUppercase 不是有效的“对特定类型的任意对象的实例方法的引用”AppTest::makeUppercase 必须实现接口 Function<AppTest,String> 才能成为有效引用。

详情:

Java 中有 4 种方法引用:

  1. ContainingClass::staticMethodName - 对静态方法的引用
  2. containingObject::instanceMethodName - 对特定对象的实例方法的引用
  3. ContainingType::methodName - 对特定类型的任意对象的实例方法的引用
  4. ClassName::new - 对构造函数的引用

每一种方法引用都需要对应的 Function 接口实现。 您可以使用对特定类型的任意对象的实例方法的引用作为参数。 这种方法引用在方法引用中没有显式参数变量,需要实现接口Function<ContainingType,String>。换句话说,左操作数的类型必须是 AppTest 才能使 AppTest::makeUppercase 可编译。 String::toUpperCase 正常工作,因为参数类型和实例类型相同 - String

import static java.lang.System.out;

import java.util.Arrays;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;

class ReferenceSource {

    private String value;

    public ReferenceSource() {
    }

    public ReferenceSource(String value) {
        this.value = value;
    }

    public String doInstanceMethodOfParticularObject(final String value) {
        return ReferenceSource.toUpperCase(value);
    }

    public static String doStaticMethod(final String value) {
        return ReferenceSource.toUpperCase(value);
    }

    public String doInstanceMethodOfArbitraryObjectOfParticularType() {
        return ReferenceSource.toUpperCase(this.value);
    }

    private static String toUpperCase(final String value) {
        return Optional.ofNullable(value).map(String::toUpperCase).orElse("");
    }
}

public class Main {
    public static void main(String... args) {
        // #1 Ref. to a constructor
        final Supplier<ReferenceSource> refConstructor = ReferenceSource::new;
        final Function<String,ReferenceSource> refParameterizedConstructor = value -> new ReferenceSource(value);

        final ReferenceSource methodReferenceInstance = refConstructor.get();

        // #2 Ref. to an instance method of a particular object
        final UnaryOperator<String> refInstanceMethodOfParticularObject = methodReferenceInstance::doInstanceMethodOfParticularObject;

        // #3 Ref. to a static method
        final UnaryOperator<String> refStaticMethod = ReferenceSource::doStaticMethod;

        // #4 Ref. to an instance method of an arbitrary object of a particular type
        final Function<ReferenceSource,String> refInstanceMethodOfArbitraryObjectOfParticularType = ReferenceSource::doInstanceMethodOfArbitraryObjectOfParticularType;

        Arrays.stream(new String[] { "a","b","c" }).map(refInstanceMethodOfParticularObject).forEach(out::print);
        Arrays.stream(new String[] { "d","e","f" }).map(refStaticMethod).forEach(out::print);
        Arrays.stream(new String[] { "g","h","i" }).map(refParameterizedConstructor).map(refInstanceMethodOfArbitraryObjectOfParticularType)
                .forEach(out::print);
    }
}

此外,您可以查看 thisthat 线程。

,
String::toUpperCase

的简短版本
text -> {
    return text.toUpperCase();
}

又是

的简短版本
new Functon<String,String> (String text) {
    Override
    public String apply(String text) {
        return text.toUpperCase();
    }
}

所以当你想要 AppTest::myMethod

你需要

public class AppTest {

    public String myMethod(){
        return this.toString();
    }

    public void printFormattedString2(AppTest appTest,Function<AppTest,String> formatter){
        System.out.println(formatter.apply(appTest));
    }

    public static void main(String[] args) {
        AppTest appTest = new AppTest();

        appTest.printFormattedString2(appTest,AppTest::myMethod);
    }
}

因为整个版本看起来如此

appTest.printFormattedString2(appTest,new Function<AppTest,String>() {
    @Override
    public String apply(AppTest text) {
        return text.makeUppercase2();
    }
});
,

为简单起见,让我们按如下方式编辑您的类。

public class AppTest {
    private String name;
    public AppTest(String name){ this.name = name; }

    public String makeUppercase() { //I have removed the argument here!!
        return this.name.toUpperCase();
    }
    psvm main(){
        AppTest appTest = new AppTest("Hello");
        Stream.of(appTest).map(AppTest::makeUppercase).forEach(System.out::println); 
        //Here makeUppercase works of objects of type AppData similar to how String::toUpperCase works on object of type String!
    }
}

这是可以接受的。为什么?

此处,AppTest::makeUppercase 是对 thisAppTest 实例进行操作的实例方法。

为什么你的不工作?

appTest.printFormattedString(source,AppTest::makeUppercase);

这不起作用,因为您需要传递 Function 的实现。而且,makeUpperCase() Function 不能从非静态上下文访问,因为方法 makeUpperCase() 作用于 AppData 类型的对象。因此,您需要 AppData 实例来调用此方法!

也许您应该将您的方法更改为静态并像这样使用它,

appTest.printFormattedString("Hello",AppTest::makeUppercase);

为什么下面的代码有效?

appTest.printFormattedString(source,appTest::makeUppercase);

因为,您创建了一个 AppTest 实例并访问了 makeUppercase 方法(即实现)并将其作为参数传递给 printFormattedString

您需要特定类型的对象来访问非静态方法。但是,您不需要特定类型的对象来访问静态方法。

String::toUpperCase 适用于 String 的实例。但是,如果没有要处理的 String 对象,则无法访问此方法。请参阅我在代码块中的注释以更好地理解这一点。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...