Java lambda表达式,转换和比较器

Jos*_*lds 21 java lambda casting java-8

我正在查看Map界面的Java源代码,并遇到了这一小段代码:

    /**
     * Returns a comparator that compares {@link Map.Entry} in natural order on value.
     *
     * <p>The returned comparator is serializable and throws {@link
     * NullPointerException} when comparing an entry with null values.
     *
     * @param <K> the type of the map keys
     * @param <V> the {@link Comparable} type of the map values
     * @return a comparator that compares {@link Map.Entry} in natural order on value.
     * @see Comparable
     * @since 1.8
     */
    public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
        return (Comparator<Map.Entry<K, V>> & Serializable)
            (c1, c2) -> c1.getValue().compareTo(c2.getValue());
    }
Run Code Online (Sandbox Code Playgroud)

从方法声明中我得到的是,这是一个泛型方法,它返回一个类型的Comparator,该类型可以从传递给它的映射条目推断出来,也可以在方法中明确提供.

什么真正让我失望的是回报价值.看来lambda表达式

(c1, c2) -> c1.getValue().compareTo(c2.getValue());
Run Code Online (Sandbox Code Playgroud)

显式地转换为Comparator<Map.Entry<K, V>>.这是正确的吗?

我也注意到明显的演员表包括& Serializable.我之前从未见过一个接口与一个类中的类相结合,但它看起来像编译器中的以下有效:

((SubClass & AnInterface) anObject).interfaceMethod();

虽然以下不起作用:

public class Foo {
    public static void main(String[] args) {
        Object o = new Foo() {
            public void bar() {
                System.out.println("nope");
            }
        };
        ((Foo & Bar) o).bar();
    }   
}

interface Bar {
    public void bar();
}
Run Code Online (Sandbox Code Playgroud)

那么,有两个问题:

  1. 如何为演员表添加界面?这只是强制执行接口方法的返回类型吗?

  2. 你能把一个Lambda表达式转换为Comparator?还有什么可以演绎的呢?或者lambda表达式基本上只是一个Comparator?有人可以澄清这一切吗?

Jat*_*tin 16

尽管彼得已经给出了一个很好的答案,但是为了进一

lambda仅在初始化时获得其精确类型.这基于目标类型.例如:

Comparator<Integer> comp = (Integer c1, Integer c2) -> c1.compareTo(c2);
BiFunction<Integer, Integer, Integer> c = (Integer c1, Integer c2) -> c1.compareTo(c2);
Comparator<Integer> compS = (Comparator<Integer> & Serializable) (Integer c1, Integer c2) -> c1.compareTo(c2);
Run Code Online (Sandbox Code Playgroud)

在所有3种情况下,它在相同的lambda之上,但它根据您提供的引用类型得到它的类型.因此,您可以在每种情况下将相同的lambda设置为3种不同的类型.

但请注意,一旦设置了类型(初始化时),就不能再进行更改了.它在字节码级别上被吸收.所以很明显你无法传递c给一个期望的方法,Comparator因为一旦初始化它们就像普通的java对象一样.(你可以看看这个来玩,并在旅途中生成lambdas)


所以在以下情况下:

(Comparator<Map.Entry<K, V>> & Serializable)
            (c1, c2) -> c1.getValue().compareTo(c2.getValue());
Run Code Online (Sandbox Code Playgroud)

lambda初始化为其目标类型为Comparator和Serializable.请注意方法的返回类型是正确的Comparator,但因为Serializable在初始化时也会记录它,所以即使此消息在方法签名中丢失,也始终可以序列化.

现在注意,投射到lambda不同于((Foo & Bar) o).bar();.在lambda的情况下,您正在初始化lambda,其类型为声明的目标类型.但是((Foo & Bar) o).bar();,你可以将变量输入o到Foo和Bar中.在前一种情况下,您正在设置类型.在后一种情况下,它已经有一个类型,你正在尝试将它转换为其他东西.因此在前者中,它抛出ClassCastException,因为它无法转换oBar

如何为演员表添加界面?

对于对象,就像通常一样.对于lambda,如上所述.

这只是强制执行接口方法的返回类型吗?

不,Java没有结构类型.所以没有基于该方法的特殊类型.它只是试图转向o两者SO1而且Bar由于后者而失败

你能将Lambda表达式转换为比较器吗?还有什么可以演绎的呢?或者lambda表达式本质上只是一个比较器?有人可以澄清这一切吗?

如上所述.可以根据所有接口符合该lambda的条件,将lambda 初始化为任何FunctionalInterface.在上面的例子中,你显然无法初始化(c1, c2) -> c1.compareTo(c2)谓词


Pet*_*rey 15

如何为演员表添加界面?

这有一个强制转换的语法,但它实际上是通过类型接口定义你创建的lambda的类型.也就是说,您没有创建对象的实例,然后将其转换为其他类型.

这只是强制执行接口方法的返回类型吗?

这实际上定义了lambda将在运行时构建的类型.有一个LambdaMetaFactory在运行时获取此类型,并在类型包含时生成额外的代码Serializable.

你能将Lambda表达式转换为比较器吗?

您只能将引用转换为对象已经存在的类型.在这种情况下,您要定义要创建的lambda必须是Comparator.您可以使用任何只有一个抽象方法的类型.

或者lambda表达式本质上只是一个比较器?

可以在不同的上下文和不同的接口中使用相同的lambda代码(复制+粘贴)而无需更改.它不必Comparator像JDK中的许多其他示例中那样.

一个我觉得有趣的是a上的count方法Stream.

  • 轻微的术语更正:而不是_defining_ lambda的类型,我们称之为_为lambda提供目标type_. (2认同)

Gio*_*tta 5

根据Java 语言规范,转换运算符(括号中的任何内容)可以是ReferenceType 后跟一个或多个 AdditionalBound 项,即一个或多个接口类型。此外,规范还指出,如果操作数的编译时类型可能永远不会根据转换转换规则转换为转换运算符指定的类型,则这是编译时错误

在您的情况下Foo没有实现Bar,但这一事实在编译时可能并不明显,因此您得到了一个,ClassCastException因为尽管匿名类中定义的方法与定义的方法具有相同的签名Bar,但该对象并未显式实现Bar。此外,匿名类中定义的方法是隐藏的,除非在与定义它们时相同的语句中调用,即,

new MyClass() {
  void doSomethingAwesome() { /* ... */ }
}.doSomethingAwesome();
Run Code Online (Sandbox Code Playgroud)

有效,但这不起作用:

MyClass awesome = new MyClass() {
  void doSomethingAwesome() { /* ... */ }
};
// undefined method, does not compile!
awesome.doSomethingAwesome();
Run Code Online (Sandbox Code Playgroud)