簡體   English   中英

如何返回一個 lambda 函數並通過將其作為參數傳遞給 Java 8 中的方法來使用? 為什么我不能使用 :: 運算符?

[英]How to return a lambda function and use by passing as an argument to a method in Java 8? Why I can't use :: operator?

我試圖在我的 Role 類中實現自定義驗證謂詞。 isInRealm()它返回一個Predicate<String>並且是一個基本的,只是為了表明我的意圖。

public enum Role {
    USER("ROLE_user", 1),
    CUSTOMER("ROLE_customer", 2),
//...
    public static Predicate<String> isInRealm() {
         return role -> Arrays.stream(values()).anyMatch(value -> value.value.equals(role));
    }
}

我可以在客戶端類中定義謂詞,也可以直接作為 lambda 表達式或方法引用傳遞。(1)(2) 下面,有它們的情況,我已經注意到哪個有效或無效。

public class UserService {

//...

public User getUserProfile() {
        //validateUser(Role.isInRealm());    -> OK
        validateUser(Role::isInRealm);       -> NOK
        validateUser(r -> Role.isInRealm()); -> NOK
        //validateUser(role -> Arrays.stream(Role.values()).anyMatch(value -> value.equals(role))); -> OK (1)
        //validateUser(s -> true); -> OK (2)
//...
}

private void validateUser(Predicate<String> predicate) {
        Authentication principal = SecurityContextHolder.getContext().getAuthentication();
        String role = utils.getRoles(principal).findFirst().get().getAuthority();
        if (!predicate.test(role)) {
            throw new ForbiddenAccessException(ResponseCode.FORBIDDEN_ACCESS.getMessage());
        }
    }
}

我對如何獲取從方法返回的Function的句柄感到困惑。

雖然isInRealm()方法返回一個 lambda 表達式作為謂詞,為什么我不能用Role::isInRealmr -> Role.isInRealm()調用它? 當我嘗試時,我bad return type in lambda expression: Predicate<String> cannot be converted to boolean收到錯誤bad return type in lambda expression: Predicate<String> cannot be converted to boolean的錯誤bad return type in lambda expression: Predicate<String> cannot be converted to boolean

當我直接在客戶端代碼中傳遞 lambda 表達式時,它可以工作,但在這種情況下,我需要使用validateUser(Role.isInRealm())獲取返回的函數。 為什么會這樣?

Role.isInRealm()是有道理的,因為該方法返回一個我們正在獲取對象的函數對象,但由於 Java 8 流 API 函數不是一流的語言構造,我無法完全掌握使用::獲取它時發生的情況。

當我們得到點或雙冒號時,后面發生了什么?

您將 lambda 與調用混合在一起。 在以下示例中調用isInRealm ,並使用返回值 (lambda):

validateUser(Role.isInRealm());

如此有效的role -> Arrays.stream(values()).anyMatch(value -> value.value.equals(role)); 傳遞給validateUser方法。

在接下來的兩個例子中:

validateUser(Role::isInRealm);
validateUser(r -> Role.isInRealm());

您正在嘗試傳遞 lambdas(供應商),調用時將返回您正在尋找的 lambda。 您沒有將Predicate傳遞給validateUser

方法validateUser需要Predicate<String> ,因此方法句柄( ::語法)和 lambda 必須符合該功能接口。

對於符合Predicate<String>的方法句柄,可以匹配三種可能的方法簽名(和返回類型):

  1. String的(非靜態) boolean methodName() ,簡稱String::methodName
  2. 任何類中的(靜態) boolean methodName(String value)被稱為ClassName::methodName
  3. 任何類中的(非靜態) boolean methodName(String value)稱為someInstance::methodName

Role.isInRealm()返回一個謂詞,因此它本身不能用作謂詞,因為它具有Predicate<String> methodName()作為其簽名和返回類型。 所以Role::isInRealm不能工作。

同樣r -> Role.isInRealm()將不起作用,因為這是Function<String, Predicate<String>>形式的 lambda(或者可能更正確: Function<?, Predicate<String>>因為沒有什么可推斷r的類型),而不是Predicate<String>

如果你想使用isInRealm作為謂詞本身,那么你需要把它寫成:

public static boolean isInRealm(String role) {
     return Arrays.stream(values()).anyMatch(value -> value.value.equals(role));
}

請注意,它現在返回一個布爾結果,而不是返回一個 lambda。

然后你可以使用以下方法調用它:

validateUser(Role::isInRealm);
// notice how it passes r to isInRealm
validateUser(r -> Role.isInRealm(r));

方法引用基本上是 lambda,您需要將某些東西傳遞給 lambda 才能使其工作,因此您的方法需要接受一個參數,並且需要有一個它消耗的值

(String s) -> System.out.println(s) 

這里的 println 方法接受一個參數,你傳遞給它一個實際值,所以你可以在這里使用方法引用

System.out::println

但在您的情況下,我認為最好將謂詞保存在變量中而不是從方法中返回它

public static Predicate<String> isInRealm = role -> Arrays.stream(values()).anyMatch(value -> value.name().equals(role));

然后你可以像這樣調用validate方法

validateUser(Role.isInRealm)

暫無
暫無

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

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