【问题标题】:Lambda Expression and generic defined only in methodLambda 表达式和泛型仅在方法中定义
【发布时间】:2014-04-30 13:50:50
【问题描述】:

假设我有一个通用接口:

interface MyComparable<T extends Comparable<T>>  {
    public int compare(T obj1, T obj2);
}

还有一个方法sort

public static <T extends Comparable<T>> 
       void sort(List<T> list, MyComparable<T> comp) {
    // sort the list
}

我可以调用此方法并将 lambda 表达式作为参数传递:

List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));

这样就可以了。

但是现在如果我将接口设为非泛型,而将方法设为泛型:

interface MyComparable {
    public <T extends Comparable<T>> int compare(T obj1, T obj2);
}

public static <T extends Comparable<T>> 
       void sort(List<T> list, MyComparable comp) {
}

然后像这样调用:

List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));

它无法编译。它在 lambda 表达式中显示错误:

“目标方法是通用的”

好的,当我使用javac 编译它时,它显示以下错误:

SO.java:20: error: incompatible types: cannot infer type-variable(s) T#1
        sort(list, (a, b) -> a.compareTo(b));
            ^
    (argument mismatch; invalid functional descriptor for lambda expression
      method <T#2>(T#2,T#2)int in interface MyComparable is generic)
  where T#1,T#2 are type-variables:
    T#1 extends Comparable<T#1> declared in method <T#1>sort(List<T#1>,MyComparable)
    T#2 extends Comparable<T#2> declared in method <T#2>compare(T#2,T#2)
1 error

从这个错误消息中,编译器似乎无法推断类型参数。是这样吗?如果是,那为什么会这样呢?

我尝试了各种方法,通过互联网搜索。然后找到了this JavaCodeGeeks article,说明了一个办法,于是试了一下:

sort(list, <T extends Comparable<T>>(a, b) -> a.compareTo(b));

这又是行不通的,这与那篇文章声称的行不通。可能它曾经在某些初始版本中工作。

所以我的问题是:有没有办法为泛型方法创建 lambda 表达式?不过,我可以通过创建方法来使用方法引用来做到这一点:

public static <T extends Comparable<T>> int compare(T obj1, T obj2) {
    return obj1.compareTo(obj2);
}

在某些类中说SO,并将其传递为:

sort(list, SO::compare);

【问题讨论】:

    标签: java generics lambda java-8


    【解决方案1】:

    如果功能接口中的方法具有类型参数,则不能将lambda表达式用于功能接口 。见section §15.27.3 in JLS8

    如果 T 是函数式接口类型(第 9.8 节)并且表达式是 全等的,则 lambda 表达式与目标类型 T 兼容 [..] 函数类型为 [..] T. [..] 如果满足以下所有条件,则 lambda 表达式与函数类型全等 真的:

    • 函数类型没有类型参数
    • [..]

    【讨论】:

    • 但是,此限制不适用于对泛型方法的方法引用。您可以使用对具有通用功能接口的通用方法的方法引用。
    • 我确信这种限制是有充分理由的。这是什么?
    • @Sandro:根本没有为 lambda 表达式声明类型参数的语法。而且这样的语法会非常复杂。请记住,解析器仍然必须能够区分带有类型参数的这种 lambda 表达式,而不是其他合法的 Java 构造。因此,您必须求助于方法引用。目标方法可以使用已建立的语法声明类型参数。
    • @Holger 仍然可以自动推断类型参数,编译器可以像您声明例如设置> 并对这些捕获的类型进行类型检查。当然,这使得在正文中将它们作为类型参数提供是不可能的,但如果您需要,诉诸方法引用是一个不错的选择
    • 注意 lambda 在许多其他方面与泛型兼容(没有方法类型参数的方式)...
    【解决方案2】:

    使用方法引用,我找到了其他传递参数的方法:

    List<String> list = Arrays.asList("a", "b", "c");        
    sort(list, Comparable::<String>compareTo);
    

    【讨论】:

      【解决方案3】:

      只需将编译器指向正确版本的通用比较器 (Comparator&lt;String&gt;)

      所以答案是

      sort(list, (Comparator&lt;String&gt;)(a, b) -&gt; a.compareTo(b));

      【讨论】:

      • incompatible types: java.util.Comparator&lt;java.lang.String&gt; cannot be converted to MyComparableMyComparable 不是通用的(无类型),所以 (MyComparable&lt;String&gt;) 也不起作用
      • 不知道你是如何输入代码的@CarlosHeuberger,但它对我很有用,这就是我想要的。
      • @IvanPeralesM。 2 个月后...我复制并粘贴了您的代码,并在您的排序行上方复制并粘贴了 - 就是这样,就像这里:ideone.com/YNwBbF!你确定你确实输入了上面的代码吗?使用Compartor ?
      • 不,我没有,我使用答案背后的想法,转换函数来告诉编译它是什么类型并且它有效。
      • @IvanPeralesM。好吧,那么您对我的“打字”有什么问题?发布的答案无效。
      【解决方案4】:

      你的意思是这样的?:

      <T,S>(T t, S s)->...
      

      这个 lambda 是什么类型的?您无法在 Java 中表达这一点,因此无法在函数应用程序中组合此表达式,并且表达式必须是可组合的。

      为了实现这项工作,您需要在 Java 中支持 Rank2 Types

      方法可以是泛型的,但您不能将它们用作表达式。但是,通过在传递它们之前专门化所有必要的泛型类型,可以将它们简化为 lambda 表达式:ClassName::&lt;TypeName&gt;methodName

      【讨论】:

      • "Of what type is this lambda? You couldn't express that in Java..." 将使用上下文推断类型,就像任何其他 lambda 一样。 lambda 的类型并没有在 lambda 本身中明确表示。
      【解决方案5】:
      List<String> list = Arrays.asList("a", "b", "c");        
      sort(list, Comparable::<String>compareTo);
      

      int compareTo (T o) 不是通用方法调用。虽然Comparable&lt;T&gt; 是一个带有类型的接口。即使compareTo 已经返回T,即T compareTo (T o),它仍然不是一个通用方法。要使其成为通用方法,它需要包含type parameters 的列表,即&lt;T&gt; T compareTo (T o)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多