Update:个
正如@Slaw在注释中指出的那样,在这种情况下,getClass()
能够向编译器提供有关泛型类型的信息.
根据前documentation名:
实际的结果类型是Class<? extends |X|>
,其中|X|
是对其调用getclass的表达式的静态类型的擦除.
因此,在编译类型中,我们将拥有? extends Interface
,并且所观察到的行为仅与Java中的type inference的特性有关.
在这种情况下,当我们在map()
次操作之后链接方法时,编译器无法根据流返回的结果类型正确推断方法引用Interface::getClass
的类型.
如果我们用collect(Collectors.toList())
替换toList
(它需要类型T
的元素并产生List<T>
),而collect(Collectors.toList())
的收集器是Collector<? super T, A, R>
类型的,编译器将能够完成它的工作(这里是proof):
List<Class<? extends Interface>> myList = myMap.entrySet().stream()
.filter(entry -> Objects.equals(entry.getValue(), myValue))
.map(Map.Entry::getKey) // Stream<Interface>
.map(Interface::getClass) // Stream<Class<capture of ? extends Interface>>
.distinct()
.collect(Collectors.toList());
但要使类型推断与toList()
一起工作,我们需要提供泛型类型explicitly.
例如,下面的代码将进行编译,因为可以从赋值上下文推断出Interface::getClass
的类型(这里map()
之后没有操作,因此myStream
直接说明应该是map()
的返回类型):
Stream<Class<? extends Interface>> myStream = myMap.entrySet().stream()
.filter(entry -> Objects.equals(entry.getValue(), myValue))
.map(Map.Entry::getKey)
.map(Interface::getClass);
List<Class<? extends Interface>> myList = myStream.distinct().toList();
一种更方便的方法是使用所谓的Type Witness:
Map<Interface, Integer> myMap = Map.of(new ClasA(), 1, new ClasB(), 1);
int myValue = 1;
List<Class<? extends Interface>> myList = myMap.entrySet().stream()
.filter(entry -> Objects.equals(entry.getValue(), myValue))
.map(Map.Entry::getKey) // Stream<Interface>
.<Class<? extends Interface>>map(Interface::getClass) // Stream<Class<? extends Interface>>
.distinct()
.toList();
myList.forEach(c -> System.out.println(c.getSimpleName()));
Output:个
ClasA
ClasB
Dummy classes:个
interface Interface {}
class ClasA implements Interface {}
class ClasB implements Interface {}