我有一个名单。在第3行,我必须将lambda表达式的结果转换为Predicate<String>。我正在阅读的这本书解释说,强制转换是必要的,以便帮助编译器确定匹配的函数接口是什么。
但是,我不需要在下面的代码行中进行这样的强制转换,因为我不调用negate()。这有什么不同呢?我知道这里的negate()返回Predicate<String>,但是前面的lambda表达式不是这样做的吗?
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发布于 2020-02-22 10:05:38
这不仅仅是因为您调用了negate()。看看这个版本,它与您的版本非常接近,但确实可以编译:
Predicate<String> predicate = str -> str.length() <= 5;
names.removeIf(predicate.negate());这和你的版本有什么不同?这是关于lambda表达式如何获取它们的类型(“目标类型”)。
你认为这是做什么的?
(str -> str.length() <= 5).negate()?您当前的答案是“它在表达式str -> str.length() <= 5给出的Predicate<String>上调用negate()”。对吗?但那只是因为这就是你想要做的。编译器并不知道这个。为什么?因为它可能是任何东西。我自己对上述问题的回答可能是:“它在我的其他函数接口类型上调用negate……(是的,这个例子有点奇怪)。”
interface SentenceExpression {
boolean checkGrammar();
default SentenceExpression negate() {
return ArtificialIntelligence.contradict(explainSentence());
};
}我可以使用完全相同的λ表达式names.removeIf((str -> str.length() <= 5).negate());,但意思是str -> str.length() <= 5是SentenceExpression而不是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 (否则,就像我给出的第一个反例一样)。
发布于 2020-02-23 04:10:00
我不知道为什么这会如此令人困惑。这可以用两个原因来解释,IMO。
我会让你弄清楚这是什么意思,以及围绕它的JLS单词是什么。但本质上,这些就像泛型一样:
static class Me<T> {
T t...
}这里的T类型是什么?好吧,这要看情况。如果您这样做了:
Me<Integer> me = new Me<>(); // it's Integer
Me<String> m2 = new Me<>(); // it's String据说,poly表达式取决于它们使用的位置的上下文。Lambda表达式是相同的。让我们将lambda表达式孤立地放在这里:
(String str) -> str.length() <= 5当你看它的时候,这是什么?这是个Predicate<String>吗?但可能是Function<String, Boolean>?或者甚至可以是MyTransformer<String, Boolean>,其中:
interface MyTransformer<String, Boolean> {
Boolean transform(String in){
// do something here with "in"
}
} 选择是无止境的。
理论上,
.negate()可能是一种选择。从上面的10_000英里来看,您是正确的:您将该str -> str.length() <= 5提供给一个只接受Predicate的removeIf方法。没有更多的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根本就不是那种类型。
发布于 2020-02-24 00:19:25
在以下示例中
names.removeIf(str -> str.length() <= 5); //compiles without cast该表达式返回true。如果在以下不带强制转换的示例中,true对negate()方法一无所知
另一方面,
names.removeIf(((Predicate<String>) str -> str.length() <= 5).negate()); //cast required表达式被强制转换为Predicate<String>,以告诉编译器在哪里可以找到方法negate。然后是negate()方法,它实际上通过以下方式进行评估:
(s)->!test(s) where s is the string argument请注意,您可以在没有强制转换的情况下获得相同的结果,如下所示:
names.removeIf(str->!str.length <= 5)
// or
name.removeIf(str->str.length > 5)https://stackoverflow.com/questions/60348254
复制相似问题