我有一个名字列表。在第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
不是完全只是因为您打电话给negate()
。看一下这个版本,它与您的版本非常接近,但是可以编译:
Predicate<String> predicate = str -> str.length() <= 5;
names.removeIf(predicate.negate());
此版本与您的版本之间的区别?关于lambda表达式如何获取其类型(“目标类型”)。
您认为这是什么?
(str -> str.length() <= 5).negate()?
您当前的答案是“它在由表达式negate()
给出的Predicate<String>
上调用str -> str.length() <= 5
”。对?但这仅仅是因为这就是您的意图。 编译器不知道。为什么?因为它可以是任何东西。我对上述问题的回答可能是“它在我的其他功能接口类型上调用negate
...(是的,示例将有些奇怪)
interface SentenceExpression {
boolean checkGrammar();
default SentenceExpression negate() {
return ArtificialIntelligence.contradict(explainSentence());
};
}
我可以使用完全相同的lambda表达式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
推断目标类型的上下文之一(如果您阅读了这些文章的答案)是调用上下文,在该上下文中,您传递了一个lambda表达式作为功能接口类型的参数的参数,这就是适用于names.removeIf(((str -> str.length() <= 5)));
:这只是作为采用Predicate<String>
的方法的参数而给出的lambda表达式。这不适用于未编译的语句。
所以,换句话说...
[names.removeIf(str -> str.length() <= 5);
在参数类型清楚地定义了lambda表达式的类型的地方使用了lambda表达式(即str -> str.length() <= 5
的目标类型显然是Predicate<String>
)。
但是,(str -> str.length() <= 5).negate()
不是lambda表达式,它只是一个碰巧使用lambda表达式的表达式。就是说,在这种情况下,str -> str.length() <= 5
不在确定lambda表达式的目标类型的调用上下文中(就像您上一条语句一样)。是的,编译器知道removeIf
需要一个Predicate<String>
,并且肯定知道传递给该方法的整个表达式都必须是Predicate<String>
,但是不会假定参数表达式中的任何lambda表达式将是一个Predicate<String>
(即使您通过在其上调用negate()
将其作为谓词;它可能是与lambda表达式兼容的任何事物)。
这就是为什么需要使用显式强制转换(或其他方式,如我所给出的第一个反例)键入lambda的原因。
我不知道为什么这必须如此令人困惑。 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表达式据说它们取决于使用它们的context。 Lambda表达式相同。让我们在isolation中使用lambda表达式:
(String str) -> str.length() <= 5
当您查看它时,这是什么?是Predicate<String>
吗?但是可能是A 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
设置为仅接受removeIf
的Predicate
方法。没有removeIf
方法,因此在提供(str -> str.length() <= 5).negate()
时,编译器应该能够“做正确的事”。
所以这怎么不起作用?让我们从您的评论开始:
对negate()的调用是否应该提供更多上下文,从而使显式强制转换的必要性降低?
似乎这是主要问题开始的地方,这根本不是javac
的工作方式。它不能取整个str -> str.length() <= 5).negate()
,告诉自己这是一个Predicate<String>
(因为您将其用作removeIf
的参数),然后进一步分解了没有.negate()
的那一部分,看是否是Predicate<String>
也。 javac
的作用相反,它需要知道target才能知道调用negate
是否合法。
通常,您还需要明确区分poly expressions和expressions。 str -> str.length() <= 5).negate()
是一个表达式,str -> str.length() <= 5
是一个多边形表达式。
[在某些语言中,可能会以不同的方式完成任务,并且在可能的情况下,javac
根本不是那种类型。
在以下示例中
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()
:
!test(s) where s is the string argument
请注意,如果不进行强制转换,则可以实现相同的结果,如下所示:
names.removeIf(str->!str.length <= 5)
// or
name.removeIf(str->str.length > 5)