为什么在Lambda表达式中使用&符号进行转换?

3

最近我在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的用途。我也不明白为什么要以这种方式转换任何内容,或者我漏看了什么吗?
在我看来,如果我想将对象强制转换为多个可能的类型,我可以像这样引入另一种泛型类型:
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。
如果你有这种类型的强制转换经验或者知道这样的转换有什么作用,任何帮助都将不胜感激。

1
你不能按照你发布的方式将类型变量 V 声明为返回类型。那就像承诺“无论调用方想要返回什么类型,我都会做到(只要该类型是 Comparator <T>Serializable 的子类型)”。你不能返回一个未知的调用者选择的类型。 - Holger
3个回答

3
由于该方法只返回Comparator接口,我不认为将其强制转换为Serializable有什么用处。
将其强制转换给类型检查器提供了一个信息,即返回的对象需要实现这两个接口。这个信息在创建lambda表达式的结果对象时会被使用。
如果没有进行强制转换,则返回的Comparator对象仅实现Comparator接口。如果比较器存储在可序列化对象的字段中,则会出现问题。
class MyClass implements Serializable {
     final Comparator<MyClass> comparator = Comparator.comparing(...)
}

如果你现在对这个类的一个实例进行序列化,比较器对象也会被序列化。如果比较器对象没有 Serializable 接口作为超类型,那么就会引发异常。


2

和符号定义了一个交集类型 - 你可以把它理解为“一个类需要同时实现 SerializableComparator<T> 接口”。


1
这种类型的转换与Java中的所有其他转换不同:如果您像这样在Java中“转换”Lambda表达式,则Lambda实际上将实现Serializable(即,这不仅会更改编译器所知道的类型,而且实际上更改对象实现的类型!)
在这种情况下,转换所做的是为Lambda表达式定义一个目标类型,它将匹配。在这种情况下,目标类型是Comparator<T>Serializable的交集,这意味着运行时Lambda实例将实现这两个接口。

谢谢您的快速回答,我不知道 lambda 运作的那种方式。 - Jeusus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接