在研究Java8 Streams时,我遇到了以下代码片段:
Predicate<? super String> predicate = s -> s.startsWith("g");
由于泛型参数是下限,我认为这不会编译。我看到它的方式,如果一个Object是String的超类型,那么传入一个Object类型应该会破坏它,因为Object没有startsWith()函数。但是,我很惊讶地看到它没有任何问题。
此外,当我调整谓词时采取上限:
<? extends String>,
它不会编译。
我以为我理解了上限和下限的含义,但显然,我错过了一些东西。任何人都可以帮助解释为什么下限适用于这个lambda?
答案 0 :(得分:8)
Lambda参数类型是精确的,不能是? super
或? extends
。这由JLS 15.27.3. Type of a Lambda Expression涵盖。它引入了地面目标类型概念(基本上是lambda类型)。其中包括:
如果
T
是通配符参数化的功能接口类型且lambda表达式是隐式类型的,那么地面目标类型是非通配符参数化(§9.9) T。
强调我的。所以基本上当你写
Predicate<? super String> predicate = s -> s.startsWith("g");
您的lambda类型为Predicate<String>
。它与:
Predicate<? super String> predicate = (Predicate<String>)(s -> s.startsWith("g"));
甚至
Predicate<String> pred = (Predicate<String>)(s -> s.startsWith("g"));
Predicate<? super String> predicate = pred;
鉴于lambdas类型参数是具体的,在适用普通类型转换规则之后:Predicate<String>
是Predicate<? super String>
或Predicate<? extends String>
。因此Predicate<? super String>
和Predicate<? extends String>
都应该编译。这两个实际上都适用于javac 8u25,8u45,8u71以及ecj 3.11.1。
答案 1 :(得分:7)
我刚测试过它,任务本身就编译了。您是否可以实际呼叫predicate.test()
。
让我们退一步,使用占位符GenericClass<T>
进行解释。对于类型参数,Foo
扩展Bar
,Bar
扩展Baz
。
扩展:
当你声明一个GenericClass<? extends Bar>
时,你会说“我不知道它的泛型类型参数究竟是什么,但它是Bar
的子类。”实际实例将始终具有非通配符类型参数,但在此部分代码中您不知道它的值是什么。现在考虑一下这对方法调用意味着什么。
你知道你实际得到的是GenericClass<Foo>
或GenericClass<Bar>
。考虑一个返回T
的方法。在前一种情况下,其返回类型为Foo
。在后者中,Bar
。无论哪种方式,它都是Bar
的子类型,可以安全地分配给Bar
变量。
考虑具有T
参数的方法。如果是GenericClass<Foo>
,则传递Bar
是错误的 - Bar
不是Foo
的子类型。
因此,使用上限可以使用泛型返回值,但不能使用泛型方法参数。
超级:
当你声明一个GenericClass<? super Bar>
时,你会说“我不知道它的泛型类型参数究竟是什么,但它是Bar
的超类。”现在考虑一下这对方法调用意味着什么。
你知道你实际得到的是GenericClass<Bar>
或GenericClass<Baz>
。考虑一个返回T
的方法。在前一种情况下,它返回Bar
。在后者中,Baz
。如果它返回Baz
,则将该值分配给Bar
变量是一个错误。你不知道它是哪一个,所以你不能安全地在这里做任何事情。
考虑具有T
参数的方法。如果是GenericClass<Bar>
,则传递Bar
是合法的。如果是GenericClass<Baz>
,则传递Bar
仍然合法,因为Bar
是Baz
的子类型。
因此,使用下限可以使用泛型方法参数,但不能使用泛型返回值。
总结:<? extends T>
表示您可以使用泛型返回值,但不能使用参数。 <? super T>
表示您可以使用通用参数但不能返回值。 Predicate.test()
有一个通用参数,因此您需要super
。
要考虑的另一件事:通配符所声明的边界是关于对象的实际类型参数。它们对与该对象一起使用的类型的后果是相反的。上限通配符(extends
)是可以为其分配返回值的变量类型的下限。下限通配符(super
)是您可以作为参数传递的类型的上限。 predicate.test(new Object())
将无法编译,因为String
的下限只会接受String
的子类。