Java 8 比较器与方法参考
Java 8 Comparator with Method reference
试图理解方法引用,但遇到了困难。
坚持使用比较器示例。
为了让我的大脑更轻松,我一直从基础开始(匿名 class 实现 >> lamba 表达式 >> 方法参考)
我无法使用方法引用和比较器示例。
我要排序的 POJO(从一些互联网示例中提取 - mkyong)
public class Fruit{
private int quantity;
private String name;
....
}
正在创建要玩的测试数据:
Fruit[] fruits = new Fruit[3];
Fruit pineapple = new Fruit("pineapple",10);
Fruit orange= new Fruit("orange",4);
Fruit kiwi= new Fruit("kiwi",15);
fruits[0] = pineapple;
fruits[1] = orange;
fruits[2] = kiwi;
匿名排序class实现:
Comparator<Fruit> anonymousSortByname = new Comparator<Fruit>(){
public int compare(Fruit f1,Fruit f2){
return f1.getName().compareTo(f2.getName());
}
};
排序:
Arrays.sort(fruits,anonymousSortByname);
现在我明白了匿名 classes 可以转换为 lambda 表达式(前提是它们是函数式接口等...)
Lambda 实现:
Comparator<Fruit> lambdaSortByname =
(Fruit f1,Fruit f2) -> f1.getName().compareTo(f2.getName());
好的,到目前为止一切都有意义。
我的理解:“无论在哪里使用 lambda,它们 都可以 替换为 'Method references'”
在 SO 和互联网上读到,使用 'Method references' 不是强制性的,但我的目标是理解它们 - 所以是否使用不是我的问题:)
也在阅读这里:
method reference1 and here and here。
但是,当我尝试使用 Comparator 实现方法参考时,我遇到了困难。
这是我尝试过的方法,它给我一个编译错误。
Comparator<Fruit> methodRefComparator = Comparator::compare;
编译错误:
Cannot make a static reference to non static block of code
在链接中,示例确实讨论了 Comparator 接口中的方法 'comparing',但此时变得模糊....
编辑#1
根据下面的评论修改了问题:
混淆是关于比较器的“比较”方法如何工作的。
我确实得到了一个工作示例(通过谷歌搜索而不是我自己的理解):
首先创建了一个接受 'Fruit' 作为输入和 returns 字符串的函数。
Function<Fruit, String> funcName = (Fruit f1) -> f1.getName();
然后将此'Function'传递给'Comparator'的'comparing'方法以取回所需的比较器。
Comparator<Fruit> sortByName = Comparator.comparing(funcName);
现在排序有效:
Arrays.sort(fruits,sortByName );
但是..我无法理解它是如何工作的。
我没有提供比较器的实现(f1.getName() - f2.getName())那么它是如何工作的?
这是否更像是一个编译器能够理解的合同,因为我已经为它提供了一个实现 'getName' 的函数,它会在返回的比较器中使用它?
当你使用->时,你可以在函数中使用变量。
使用::时,函数使用默认变量。
您的源代码编译失败,因为函数比较是用 2 个变量实现的,而您被放入了 0 个变量。
Comparator.comparing
是一个 static
工厂方法,它创建一个比较器,它将委托给您提供的函数,用于获取要比较的 属性 值。基本上是这样的:
public static <T, U extends Comparable<? super U>>
Comparator<T> comparing(Function<? super T, ? extends U> func) {
return (o1, o2) -> func.apply(o1).compareTo(func.apply(o2));
}
因此,为两个参数评估函数并用结果调用 compareTo
的逻辑就在那里。
由于这对比较 属性 元素的所有比较器都是通用的,因此自然而然地决定将通用代码放入共享方法中。这种分离也简化了您必须提供的功能,因为它只需要表达如何获取 属性 值。
因为这通常意味着只调用一个 (getter) 方法,所以它允许将函数表达为方法引用,这只有在有一个现有方法执行您的函数预期的操作时才有可能要做,即
Arrays.sort(fruits, Comparator.comparing(Fruit::getName));
其中 Fruit::getName
等同于 (Fruit f) -> f.getName()
.
因此,并非每个 lambda 表达式都可以替换为方法引用。当 lambda 表达式由单个方法调用组成,或者实用程序方法帮助您将您这边的逻辑简化为由单个方法调用组成的函数时,您可以替换它。
试图理解方法引用,但遇到了困难。 坚持使用比较器示例。
为了让我的大脑更轻松,我一直从基础开始(匿名 class 实现 >> lamba 表达式 >> 方法参考) 我无法使用方法引用和比较器示例。
我要排序的 POJO(从一些互联网示例中提取 - mkyong)
public class Fruit{
private int quantity;
private String name;
....
}
正在创建要玩的测试数据:
Fruit[] fruits = new Fruit[3];
Fruit pineapple = new Fruit("pineapple",10);
Fruit orange= new Fruit("orange",4);
Fruit kiwi= new Fruit("kiwi",15);
fruits[0] = pineapple;
fruits[1] = orange;
fruits[2] = kiwi;
匿名排序class实现:
Comparator<Fruit> anonymousSortByname = new Comparator<Fruit>(){
public int compare(Fruit f1,Fruit f2){
return f1.getName().compareTo(f2.getName());
}
};
排序:
Arrays.sort(fruits,anonymousSortByname);
现在我明白了匿名 classes 可以转换为 lambda 表达式(前提是它们是函数式接口等...)
Lambda 实现:
Comparator<Fruit> lambdaSortByname =
(Fruit f1,Fruit f2) -> f1.getName().compareTo(f2.getName());
好的,到目前为止一切都有意义。
我的理解:“无论在哪里使用 lambda,它们 都可以 替换为 'Method references'”
在 SO 和互联网上读到,使用 'Method references' 不是强制性的,但我的目标是理解它们 - 所以是否使用不是我的问题:)
也在阅读这里: method reference1 and here and here。 但是,当我尝试使用 Comparator 实现方法参考时,我遇到了困难。
这是我尝试过的方法,它给我一个编译错误。
Comparator<Fruit> methodRefComparator = Comparator::compare;
编译错误:
Cannot make a static reference to non static block of code
在链接中,示例确实讨论了 Comparator 接口中的方法 'comparing',但此时变得模糊....
编辑#1
根据下面的评论修改了问题:
混淆是关于比较器的“比较”方法如何工作的。
我确实得到了一个工作示例(通过谷歌搜索而不是我自己的理解):
首先创建了一个接受 'Fruit' 作为输入和 returns 字符串的函数。
Function<Fruit, String> funcName = (Fruit f1) -> f1.getName();
然后将此'Function'传递给'Comparator'的'comparing'方法以取回所需的比较器。
Comparator<Fruit> sortByName = Comparator.comparing(funcName);
现在排序有效:
Arrays.sort(fruits,sortByName );
但是..我无法理解它是如何工作的。 我没有提供比较器的实现(f1.getName() - f2.getName())那么它是如何工作的?
这是否更像是一个编译器能够理解的合同,因为我已经为它提供了一个实现 'getName' 的函数,它会在返回的比较器中使用它?
当你使用->时,你可以在函数中使用变量。 使用::时,函数使用默认变量。
您的源代码编译失败,因为函数比较是用 2 个变量实现的,而您被放入了 0 个变量。
Comparator.comparing
是一个 static
工厂方法,它创建一个比较器,它将委托给您提供的函数,用于获取要比较的 属性 值。基本上是这样的:
public static <T, U extends Comparable<? super U>>
Comparator<T> comparing(Function<? super T, ? extends U> func) {
return (o1, o2) -> func.apply(o1).compareTo(func.apply(o2));
}
因此,为两个参数评估函数并用结果调用 compareTo
的逻辑就在那里。
由于这对比较 属性 元素的所有比较器都是通用的,因此自然而然地决定将通用代码放入共享方法中。这种分离也简化了您必须提供的功能,因为它只需要表达如何获取 属性 值。
因为这通常意味着只调用一个 (getter) 方法,所以它允许将函数表达为方法引用,这只有在有一个现有方法执行您的函数预期的操作时才有可能要做,即
Arrays.sort(fruits, Comparator.comparing(Fruit::getName));
其中 Fruit::getName
等同于 (Fruit f) -> f.getName()
.
因此,并非每个 lambda 表达式都可以替换为方法引用。当 lambda 表达式由单个方法调用组成,或者实用程序方法帮助您将您这边的逻辑简化为由单个方法调用组成的函数时,您可以替换它。