当前位置: 首页 > 知识库问答 >
问题:

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

双子民
2023-03-14

我在查看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());
    }

从方法声明中,我得到这是一个通用方法,它返回一种类型的比较器,这种比较器要么是从传递给它的映射条目中推断出来的,要么是在方法中显式提供的。

真正让我恼火的是返回值。看来λ表达式

(c1, c2) -> c1.getValue().compareTo(c2.getValue());

显式转换为比较器

我还注意到明显的演员阵容包括

((子类)

尽管以下方法不起作用:

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();
}

所以,两个问题:

>

  • 向cast添加接口应该如何工作?这只是强制执行接口方法的返回类型吗?

    能否将Lambda表达式强制转换为比较器?他们还能扮演什么角色?或者lambda表达式本质上只是一个比较器?有人能澄清这一切吗?

  • 共有3个答案

    柯瀚海
    2023-03-14

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

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

    new MyClass() {
      void doSomethingAwesome() { /* ... */ }
    }.doSomethingAwesome();
    

    工作,但这不是:

    MyClass awesome = new MyClass() {
      void doSomethingAwesome() { /* ... */ }
    };
    // undefined method, does not compile!
    awesome.doSomethingAwesome();
    
    聂风史
    2023-03-14

    向演员添加接口应该如何工作?

    这具有强制转换的语法,但实际上它定义了通过类型接口创建的lambda的类型。也就是说,您不是在创建一个对象的实例,该对象随后将被转换为另一种类型。

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

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

    你能把Lambda表达式转换成比较器吗?

    只能对对象已经存在的类型强制转换引用。在这种情况下,定义要创建的lambda必须是比较器。可以使用只有一个抽象方法的任何类型。

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

    相同的lambda代码可以在不同的上下文和不同的接口中使用(复制粘贴),而无需更改。它不必是一个比较器,正如您在JDK的许多其他示例中看到的那样。

    我发现一个有趣的方法是流上的count方法。

    翟京
    2023-03-14

    虽然彼得给出了一个很好的答案,但为了进一步澄清,让我补充一下。

    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);
    

    在以上三种情况下,它的lambda都是相同的,但它的类型是基于您提供的引用类型获得的。因此,您可以在每个案例中将相同的lambda设置为3种不同的类型。

    但请注意,一旦设置了类型(初始化时),就不能再更改它了。它在字节码级别被吸收。所以很明显,你不能把c传递给一个需要Comparator的方法,因为一旦初始化,它们就像普通的java对象一样。(您可以查看这个类,以便在运行中四处玩耍并生成lambda)

    因此,如果:

    (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> c1.getValue().compareTo(c2.getValue());
    

    lambda以其目标类型作为比较器进行初始化,并可序列化。请注意,方法的返回类型只是比较器,但由于初始化时可序列化也被刻在它上面,所以即使该消息在方法签名中丢失,它也可以被序列化。

    现在请注意,对lambda进行强制转换不同于((Foo

    向cast添加接口应该如何工作?

    对于对象,就像正常情况下一样。对于lambda,如上所述。

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

    没有。Java没有结构类型。因此,没有基于该方法的特殊类型。它只是尝试将o转换为SO1Bar,但由于后者,它失败了

    您可以将Lambda表达式转换为比较器吗?他们还能被塑造成什么?或者λ表达式本质上只是一个比较器?有人能澄清这一切吗?

    如上所述。lambda可以根据所有接口都符合该lambda的条件初始化为任何FunctionalInterface。在上面的例子中,你显然不能初始化(c1, c2)-

     类似资料:
    • 问题内容: 我正在浏览Java源代码中的接口,并遇到了以下这段代码: 从方法声明中,我知道这是一个通用方法,该方法返回一个Comparator类型,该Comparator类型可以从传递给它的映射条目中推断出来,也可以在该方法中明确提供。 真正让我失望的是返回值。似乎lambda表达式 被显式转换为。这是正确的吗? 我还注意到,表观演员包括。我之前从未见过将接口与类组合在一起的类型,但是在编译器中它

    • 我正在尝试执行以下操作(cb=CriteriaBuilder): 但我得到以下语法错误: 绑定不匹配:(表达式之间的泛型方法 是否可以转换表达式

    • P.S.如果您将这两行代码作为结果所做的事情添加进去,那将是非常感谢的。

    • 我有下面的类和对象的ArrayList: 我想要的是从我的列表中得到一个字符串和整数地图,其中包含门票中的目的地和我列表中每个目的地的出现次数。我想它会这样开始,但是我不知道如何继续:

    • 我试图将js代码转换为java版本,但对正则表达式有点困惑:在js中,我有一个类似于:testString的表达式。拆分(“/(\w*\d)/g”) 我可以在java中使用什么等效语言?我试着用匹配器得到同样的结果。find()和split函数,但结果不一样。

    • 我还在尝试理解lambda表达式。 我想将这个lambda表达式转换为“普通”表达式: 你们有主意吗? 提前谢谢。