【问题标题】:Can BiFunction references be passed to methods expecting a functional interface?BiFunction 引用可以传递给期望函数接口的方法吗?
【发布时间】:2015-02-05 04:41:16
【问题描述】:

我一直只使用 Java 6,现在正赶上学习 Java 8 的新功能。我在这里阅读了这篇文章: http://www.drdobbs.com/jvm/lambda-expressions-in-java-8/240166764?pgno=2

它说:

Java API 在 java.util.function 包。其中一个接口 BiFunction 描述了具有参数类型 T 和 U 以及返回类型的函数 R. 您可以将我们的字符串比较 lambda 保存在该变量中 类型:

BiFunction<String, String, Integer> comp 
    = (first, second) -> Integer.compare(first.length(), second.length()); 

但是,这对您进行排序没有帮助。那里 没有需要 BiFunction 的 Arrays.sort 方法。如果您使用过 在使用函数式编程语言之前,您可能会对此感到好奇。但 对于 Java 程序员来说,这很自然。一个接口如 比较器有一个特定的目的,而不仅仅是一个给定的方法 参数和返回类型。 Java 8 保留了这种风格。当你想要的时候 要使用 lambda 表达式做某事,您仍然希望保留 记着表达的目的,并具有特定的功能 接口。

但是,当我看到这个帖子时: How do you assign a lambda to a variable in Java 8?

那里的问题的答案建议完全按照引用的段落所说的去做。

那么,是文章中的信息不正确,还是我在这里看错了什么?

谢谢!

【问题讨论】:

  • 您可以将相同的 lambda 分配给 Comparator 并在排序调用中使用它...

标签: java java-8 lambda bifunctor


【解决方案1】:

我在链接的 SO 答案中没有看到任何与文章相矛盾的内容。

类型系统的常规规则适用于功能接口

如果您将变量声明为BiFunction&lt;String,String,Integer&gt; bifunc,则不允许将其传递给需要Comparator&lt;String&gt; 的方法,因为BiFunction&lt;String,String,Integer&gt; 不是Comparator&lt;String&gt;子类型

函数类型遵循所有常规规则这一事实使得可以以最小的干扰添加这个新功能。

如果你想从BiFunction 中创建一个Comparator,你只需像这样添加::apply

BiFunction<String,String,Integer> bifunc = (a,b) -> 
                               Integer.compare(a.length(), b.length());

Arrays.sort(array, bifunc::apply);  

【讨论】:

    【解决方案2】:

    这篇文章在某种意义上是正确的,你不能根据BiFunction类型的对象进行排序,但你总是可以使用Comparator。但是,嘿,他们都可以拥有相同的身体。例如:

    private static void sort(Comparator<String> ls){
            Arrays.sort(someArray, ls);
    }
    
    Comparator<String> comp = (String f1, String f2) -> Integer.compare(f1.length(), f2.length());
    sort(comp);
    
    BiFunction<String, String, Integer> func = (String f1, String f2) -> Integer.compare(f1.length(), f2.length());
    sort((String f1, String f2) -> Integer.compare(f1.length(), f2.length())); //line-4
    
    sort(func) // compiler error
    

    在第 4 行上方,您可以传递一个与 func 完全相同的 lambda。但是您仍然不能将func 传递给sort。 java8 中的 Lambda 是一些 FunctionalInterface 的实现。功能接口根据其引用类型获取其类型。这就是初始化时相同的 lambda 可以是 BiFunctionComparator

    但是一旦构造了一个 lambda 并且它得到了它的类型,那么你就不能改变它了。因此,您不能将BiFunction 类型的func 传递给期望Comparator 的排序

    【讨论】:

      【解决方案3】:

      这篇文章是正确的。您不能分配例如BiFunctionComparator

      话虽如此,Brian Goetz 写的 great article 很好地解释了这个问题。

      当编译器遇到 lambda 表达式时,它首先将 lambda 主体降低(脱糖)为参数列表和返回类型与 lambda 表达式匹配的方法

      所以,lambda 可以去糖 - 但这意味着什么?好吧,基本上这意味着(可能)创建了一种与 lamdba 匹配的新方法。

      class A {
          public void foo() {
              List<String> list = ...
              list.forEach( s -> { System.out.println(s); } );
          }
      }
      

      上面的代码将被desugared变成这样:

      class A {
          public void foo() {
              List<String> list = ...
              list.forEach( [lambda for lambda$1 as Consumer] );
          }
      
          static void lambda$1(String s) {
              System.out.println(s);
          }
      }
      

      所以,对于BiFunctionComparator。提供的 lambda 可以分配给两者:

      // Assign the lambda to a BiFunction
      BiFunction<String, String, Integer> b1 =
              (first, second) -> Integer.compare(first.length(), second.length());
      
      // Assign the lambda to a Comparator
      Comparator<String> c1 =
              (first, second) -> Integer.compare(first.length(), second.length());
      
      // But, once the lambda has been assigned to a type you can not reassign it
      BiFunction<String, String, Integer> b2 = c1; // <-- Error
      

      请注意,一旦将 lambda 分配给一个类型(BiFunctionComparator),就不能重新分配它,即使 lambda 表达式匹配

      【讨论】:

      • 当您从这篇旧文章中接管代码 sn-ps 时,您应该抓住机会将它们更新为实际的 API。那时的Block&lt;String&gt; 变成了Consumer&lt;String&gt;。对于不了解历史的读者来说,坚持使用旧名称可能会造成混淆。
      猜你喜欢
      • 2012-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-29
      • 2015-02-09
      • 1970-01-01
      • 2011-01-06
      • 2020-10-27
      相关资源
      最近更新 更多