繁体   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