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

Method reference for static and instance methods

在 Java

中的实例方法的情况下,我无法掌握方法引用的概念

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

我看过String::toUpperCase的例子。

我对以下这一点感到困惑 (1) String是一个class,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);
    }
}
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();
    }
});

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

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是在AppTestthis实例上运行的实例方法。

为什么你的不工作?

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 对象,您将无法访问此方法。请参阅我在代码块中的评论以更好地理解这一点。

为什么不允许 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);
    }
}

此外,您可以查看 and that 话题。