簡體   English   中英

static 和實例方法的方法參考

[英]Method reference for static and instance methods

在 Java 中的實例方法的情況下,我無法掌握方法引用的概念

例如在下面的示例中,編譯器在列表行中給出錯誤。

我看過 String::toUpperCase 的例子。

我對(1)字符串是 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);
    }
}

為什么不允許AppTest::makeUppercase

簡短的回答是AppTest::makeUppercase無效“引用特定類型的任意 object 的實例方法” AppTest::makeUppercase必須實現接口Function<AppTest, String>才能成為有效引用。

細節:

Java中有4種方法參考:

  1. ContainingClass::staticMethodName - 引用 static 方法
  2. containsObject containingObject::instanceMethodName - 引用特定 object 的實例方法
  3. ContainingType::methodName - 引用特定類型的任意 object 的實例方法
  4. ClassName::new - 對構造函數的引用

每種方法引用都需要對應的Function接口實現。 您使用對特定類型的任意 object 的實例方法的引用作為參數。 這種方法引用在方法引用中沒有顯式參數變量,需要實現接口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);
    }
}

此外,您可以查看這個那個線程。

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是一個對AppTest實例進行操作this實例方法。

為什么你的不工作?

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

這不起作用,因為您需要通過Function的實現。 而且, makeUpperCase() Function無法從非靜態上下文訪問,因為方法makeUpperCase()適用於AppData類型的對象。 所以,你需要AppData實例來調用這個方法!

也許您應該將您的方法更改為 static 並像這樣使用它,

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

為什么以下代碼有效?

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

因為,您創建了AppTest的實例並訪問makeUppercase方法(這是實現)並將其作為參數傳遞給printFormattedString

您需要特定類型的對象才能訪問非靜態方法。 但是,您不需要特定類型的對象來訪問 static 方法。

String::toUpperCase適用於String的實例。 但是如果沒有String object 可以使用,則無法訪問此方法。 請參閱我在代碼塊中的評論以更好地理解這一點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM