首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么negate()需要显式转换为Predicate?

为什么negate()需要显式转换为Predicate?
EN

Stack Overflow用户
提问于 2020-02-22 09:40:01
回答 4查看 279关注 0票数 7

我有一个名单。在第3行,我必须将lambda表达式的结果转换为Predicate<String>。我正在阅读的这本书解释说,强制转换是必要的,以便帮助编译器确定匹配的函数接口是什么。

但是,我不需要在下面的代码行中进行这样的强制转换,因为我不调用negate()。这有什么不同呢?我知道这里的negate()返回Predicate<String>,但是前面的lambda表达式不是这样做的吗?

代码语言:javascript
复制
List<String> names = new ArrayList<>();
//add various names here
names.removeIf(((Predicate<String>) str -> str.length() <= 5).negate()); //cast required
names.removeIf(((str -> str.length() <= 5))); //compiles without cast
EN

回答 4

Stack Overflow用户

发布于 2020-02-22 10:05:38

这不仅仅是因为您调用了negate()。看看这个版本,它与您的版本非常接近,但确实可以编译:

代码语言:javascript
复制
Predicate<String> predicate = str -> str.length() <= 5;
names.removeIf(predicate.negate());

这和你的版本有什么不同?这是关于lambda表达式如何获取它们的类型(“目标类型”)。

你认为这是做什么的?

代码语言:javascript
复制
(str -> str.length() <= 5).negate()?

您当前的答案是“它在表达式str -> str.length() <= 5给出的Predicate<String>上调用negate()”。对吗?但那只是因为这就是你想要做的。编译器并不知道这个。为什么?因为它可能是任何东西。我自己对上述问题的回答可能是:“它在我的其他函数接口类型上调用negate……(是的,这个例子有点奇怪)。”

代码语言:javascript
复制
interface SentenceExpression {
    boolean checkGrammar();
    default SentenceExpression negate() {
        return ArtificialIntelligence.contradict(explainSentence());
    };
}

我可以使用完全相同的λ表达式names.removeIf((str -> str.length() <= 5).negate());,但意思是str -> str.length() <= 5SentenceExpression而不是Predicate<String>

说明:(str -> str.length() <= 5).negate()不会让str -> str.length() <= 5成为Predicate<String>。这就是为什么我说它可以是任何东西,包括我上面的函数接口。

回到Java...这就是为什么lambda表达式有“目标类型”的概念,它定义了编译器将lambda表达式理解为给定函数接口类型的机制(即,如何帮助编译器知道表达式是Predicate<String>而不是SentenceExpression或其他任何类型)。您可能会发现通读What is meant by lambda target type and target type context in Java?Java 8: Target typing很有用

其中一个推断目标类型的上下文(如果您阅读了这些帖子上的答案)是调用上下文,其中您将λ表达式作为函数接口类型的参数的参数传递,这也适用于names.removeIf(((str -> str.length() <= 5)));:它只是作为参数提供给接受Predicate<String>的方法的λ表达式。这不适用于未编译的语句。

所以换句话说..。

names.removeIf(str -> str.length() <= 5);在参数类型明确定义了λ表达式的类型的地方使用了λ表达式(即,str -> str.length() <= 5的目标类型明确为Predicate<String>)。

然而,(str -> str.length() <= 5).negate()不是λ表达式,它只是一个恰好使用λ表达式的表达式。这就是说,本例中的str -> str.length() <= 5不在决定lambda表达式的目标类型的调用上下文中(与上一条语句的情况相同)。是的,编译器知道removeIf需要一个Predicate<String>,并且它肯定知道传递给该方法的整个表达式必须是一个Predicate<String>,但它不会假定参数表达式中的任何λ表达式都是Predicate<String> (即使通过调用negate()将其视为谓词;它可以是与λ表达式兼容的任何内容)。

这就是为什么需要使用显式强制转换来键入lambda (否则,就像我给出的第一个反例一样)。

票数 8
EN

Stack Overflow用户

发布于 2020-02-23 04:10:00

我不知道为什么这会如此令人困惑。这可以用两个原因来解释,IMO。

  • Lambda表达式是多边形表达式。

我会让你弄清楚这是什么意思,以及围绕它的JLS单词是什么。但本质上,这些就像泛型一样:

代码语言:javascript
复制
static class Me<T> {
    T t...
}

这里的T类型是什么?好吧,这要看情况。如果您这样做了:

代码语言:javascript
复制
Me<Integer> me = new Me<>(); // it's Integer
Me<String>  m2 = new Me<>(); // it's String

据说,poly表达式取决于它们使用的位置的上下文。Lambda表达式是相同的。让我们将lambda表达式孤立地放在这里:

代码语言:javascript
复制
(String str) -> str.length() <= 5

当你看它的时候,这是什么?这是个Predicate<String>吗?但可能是Function<String, Boolean>?或者甚至可以是MyTransformer<String, Boolean>,其中:

代码语言:javascript
复制
 interface MyTransformer<String, Boolean> {
     Boolean transform(String in){
         // do something here with "in"
     } 
 } 

选择是无止境的。

理论上,

  • 直接调用.negate()可能是一种选择。

从上面的10_000英里来看,您是正确的:您将该str -> str.length() <= 5提供给一个只接受PredicateremoveIf方法。没有更多的removeIf方法,所以当您提供该(str -> str.length() <= 5).negate()时,编译器应该能够“做正确的事情”。

那么,为什么这不起作用呢?让我们从你的评论开始:

不应该调用negate()提供更多的上下文,从而使显式强制转换变得更不必要吗?

似乎这就是主要的问题所在,这根本不是javac的工作方式。它不能获取整个str -> str.length() <= 5).negate(),告诉自己这是一个Predicate<String> (因为您将它用作removeIf的参数),然后进一步分解没有.negate()的部分,看看这是否也是一个Predicate<String>javac的行为正好相反,它需要知道目标,以便能够判断调用negate是否合法。

另外,一般来说,您需要明确区分多边形表达式和表达式。str -> str.length() <= 5).negate()是一个表达式,str -> str.length() <= 5是一个多边形表达式。

有些语言可能会有不同的处理方式,在可能的情况下,javac根本就不是那种类型。

票数 4
EN

Stack Overflow用户

发布于 2020-02-24 00:19:25

在以下示例中

代码语言:javascript
复制
      names.removeIf(str -> str.length() <= 5); //compiles without cast

该表达式返回true。如果在以下不带强制转换的示例中,truenegate()方法一无所知

另一方面,

代码语言:javascript
复制
   names.removeIf(((Predicate<String>) str -> str.length() <= 5).negate()); //cast required

表达式被强制转换为Predicate<String>,以告诉编译器在哪里可以找到方法negate。然后是negate()方法,它实际上通过以下方式进行评估:

代码语言:javascript
复制
   (s)->!test(s) where s is the string argument

请注意,您可以在没有强制转换的情况下获得相同的结果,如下所示:

代码语言:javascript
复制
    names.removeIf(str->!str.length <= 5) 
      // or
    name.removeIf(str->str.length > 5)
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60348254

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档