Lambda 表达式和泛型仅在方法中定义
Posted
技术标签:
【中文标题】Lambda 表达式和泛型仅在方法中定义【英文标题】:Lambda Expression and generic defined only in method 【发布时间】: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);
【问题讨论】:
【参考方案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<String>)
所以答案是
sort(list, (Comparator<String>)(a, b) -> a.compareTo(b));
【讨论】:
incompatible types: java.util.Comparator<java.lang.String> cannot be converted to MyComparable
和 MyComparable
不是通用的(无类型),所以 (MyComparable<String>)
也不起作用
不知道你是如何输入代码的@CarlosHeuberger,但它对我很有用,这就是我想要的。
@IvanPeralesM。 2 个月后...我复制并粘贴了您的代码,并在您的排序行上方复制并粘贴了 - 就是这样,就像这里:ideone.com/YNwBbF!你确定你确实输入了上面的代码吗?使用Compartor
?
不,我没有,我使用答案背后的想法,转换函数来告诉编译它是什么类型并且它有效。
@IvanPeralesM。好吧,那么您对我的“打字”有什么问题?发布的答案无效。【参考方案4】:
你的意思是这样的?:
<T,S>(T t, S s)->...
这个 lambda 是什么类型的?您无法在 Java 中表达这一点,因此无法在函数应用程序中组合此表达式,并且表达式必须是可组合的。
为了实现这项工作,您需要在 Java 中支持 Rank2 Types。
方法可以是泛型的,但您不能将它们用作表达式。但是,通过在传递它们之前专门化所有必要的泛型类型,可以将它们简化为 lambda 表达式:ClassName::<TypeName>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<T>
是一个带有类型的接口。即使compareTo
已经返回T
,即T compareTo (T o)
,它仍然不是一个通用方法。要使其成为通用方法,它需要包含type parameters
的列表,即<T> T compareTo (T o)
。
【讨论】:
以上是关于Lambda 表达式和泛型仅在方法中定义的主要内容,如果未能解决你的问题,请参考以下文章
Java之collection集合常见数据结构List和泛型
C#委托,匿名方法,Lambda,泛型委托,表达式树代码示例
Java8函数式接口/Lambda表达式/接口默认方法/接口静态方法/接口冲突方法重写/lambda表达式指定泛型类型等