问题描述
最近我偶然发现了Java Comparator类中的以下代码:
public static <T,U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T,? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1,c2) -> keyExtractor.apply(c1).compareto(keyExtractor.apply(c2));
}
让我感到困惑的是(Comparator<T> & Serializable)
部分。由于该方法仅返回Comparator,因此在强制转换为Serializable时看不到它的用途。我也看不出以这种方式投放任何内容的原因,还是我错过了一些东西?
在我看来,如果我想将一个Object转换为多种可能的类型,则可以引入另一个通用类型,如下所示:
public static <T,U extends Comparable<? super U>,V extends Comparator<T> & Serializable>
V comparing(Function<? super T,? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (V) (c1,c2) -> keyExtractor.apply(c1).compareto(keyExtractor.apply(c2));
}
通过这种方式,可以将返回值分配给Comparator或Serializable变量。
我能想到的唯一另一个原因是,将这种类型转换用作某种类型检查,以查看lambda表达式是否实际上返回了Serializable。
如果您有任何使用这种类型的转换的经验,或者对这种转换可以完成的想法有任何想法,将不胜感激。
解决方法
由于该方法仅返回Comparator,因此在转换为Serializable时看不到它的用途。
强制类型转换通知类型检查器返回的对象需要实现两个接口。创建lambda表达式结果的对象时将使用此信息。
在不进行强制转换的情况下,返回的Comparator
对象仅实现{strong>仅的Comparator
接口。如果比较器存储在可序列化对象的字段中,则会出现问题:
class MyClass implements Serializable {
final Comparator<MyClass> comparator = Comparator.comparing(...)
}
如果现在序列化此类的实例,则比较器对象也将序列化。如果比较器对象没有Serializable接口作为超类型,则将获得异常。
,&符定义了一个交集类型-您可以将其读为“需要同时实现Serializable
和Comparator<T>
接口的类”。
这种类型转换的怪异之处在于,它与Java中的所有其他类型转换不同:如果您在Java中像这样“投射” lambda表达式,则lambda实际上会实现Serializable
(即,它不会只需更改编译器知道的类型,但是实际上会更改对象实现的类型!)
在这种情况下,类型转换的作用是为lambda表达式定义一个将匹配的目标类型。在这种情况下,目标类型是Comparator<T>
和Serializable
的交集,这意味着在运行时lambda实例将实现这两个接口。