在Java 8中,双冒号(::)运算符称为方法引用。 请参考以下示例:
匿名类打印列表。
List list = Arrays.asList("node", "java", "python", "ruby");
list.forEach(new Consumer() { // anonymous class
@Override
public void accept(String str) {
System.out.println(str);
}
});
匿名类-> Lambda表达式。
List list = Arrays.asList("node", "java", "python", "ruby");
list.forEach(str -> System.out.println(str)); // lambda
Lambda表达式->方法引用。
List list = Arrays.asList("node", "java", "python", "ruby");
list.forEach(System.out::println); // method references
匿名类-> Lambda表达式->方法参考
注意
lambda表达式或方法引用都不执行任何操作,只是对现有方法的另一种调用。 使用方法参考,可以获得更好的可读性。
有四种方法参考:
- 引用静态方法
ClassName::staticMethodName
- 引用特定对象
Object::instanceMethodName
的实例方法 - 引用特定类型
ContainingType::methodName
的任意对象的实例方法– - 引用构造函数
ClassName::new
1.静态方法
Lambda表达式。
(args) -> ClassName.staticMethodName(args)
方法参考。
ClassName::staticMethodName
1.1此示例打印一个字符串列表,该列表引用了静态方法SimplePrinter::print
。
package com.mkyong;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class Java8MethodReference1a {
public static void main(String[] args) {
List list = Arrays.asList("A", "B", "C");
// anonymous class
list.forEach(new Consumer() {
@Override
public void accept(String x) {
SimplePrinter.print(x);
}
});
// lambda expression
list.forEach(x -> SimplePrinter.print(x));
// method reference
list.forEach(SimplePrinter::print);
}
}
class SimplePrinter {
public static void print(String str) {
System.out.println(str);
}
}
1.2此示例将字符串列表转换为整数列表,该方法引用静态方法Integer::parseInt
。
public static int parseInt(String s) throws NumberFormatException {
return parseInt(s,10);
}
package com.mkyong;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Java8MethodReference1b {
public static void main(String[] args) {
List list = Arrays.asList("1", "2", "3");
// anonymous class
List collect1 = list.stream()
.map(new Function() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s);
}
})
.collect(Collectors.toList());
// lambda expression
List collect2 = list.stream()
.map(s -> Integer.parseInt(s))
.collect(Collectors.toList());
// method reference
List collect3 = list.stream()
.map(Integer::parseInt)
.collect(Collectors.toList());
}
}
1.3本示例将两个Integer
连接起来并返回String
。 它将方法引用静态方法IntegerUtils::join
作为参数传递给另一个接受BiFunction
方法。
package com.mkyong;
import java.util.function.BiFunction;
public class Java8MethodReference1c {
public static void main(String[] args) {
// anonymous class
String result1 = playTwoArgument(1, 2, new BiFunction() {
@Override
public String apply(Integer a, Integer b) {
return IntegerUtils.join(a, b);
}
}); // 3
// lambda
String result1 = playTwoArgument(1, 2, (a, b) -> IntegerUtils.join(a, b)); // 3
// method reference
String result2 = playTwoArgument(1, 2, IntegerUtils::join); // 3
}
private static R playTwoArgument(Integer i1, Integer i2,
BiFunction func) {
return func.apply(i1, i2);
}
}
class IntegerUtils{
public static String join(Integer a, Integer b) {
return String.valueOf(a + b);
}
}
2.引用特定对象的实例方法
Lambda表达式。
(args) -> object.instanceMethodName(args)
方法参考。
object::instanceMethodName
2.1此示例按薪水对Employee
列表进行排序。 我们可以引用特定对象ComparatorProvider
的实例方法compareBySalary
。
package com.mkyong;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
public class Java8MethodReference2 {
public static void main(String[] args) {
List list = Arrays.asList(
new Employee("mkyong", 38, BigDecimal.valueOf(3800)),
new Employee("zilap", 5, BigDecimal.valueOf(100)),
new Employee("ali", 25, BigDecimal.valueOf(2500)),
new Employee("unknown", 99, BigDecimal.valueOf(9999)));
// anonymous class
/*list.sort(new Comparator() {
@Override
public int compare(Employee o1, Employee o2) {
return provider.compareBySalary(o1, o2);
}
});*/
ComparatorProvider provider = new ComparatorProvider();
// lambda
// list.sort((o1, o2) -> provider.compareBySalary(o1, o2));
// method reference
list.sort(provider::compareBySalary);
list.forEach(x -> System.out.println(x));
}
}
class ComparatorProvider {
public int compareByAge(Employee o1, Employee o2) {
return o1.getAge().compareTo(o2.getAge());
}
public int compareByName(Employee o1, Employee o2) {
return o1.getName().compareTo(o2.getName());
}
public int compareBySalary(Employee o1, Employee o2) {
return o1.getAge().compareTo(o2.getAge());
}
}
package com.mkyong;
import java.math.BigDecimal;
public class Employee {
String name;
Integer age;
BigDecimal salary;
// generated by IDE, getters, setters, constructor, toString
}
输出量
Employee{name='zilap', age=5, salary=100}
Employee{name='ali', age=25, salary=2500}
Employee{name='mkyong', age=38, salary=3800}
Employee{name='unknown', age=99, salary=9999}
3.引用特定类型的任意对象的实例方法。
该语句有点混乱,需要很少的解释,请参见以下示例:
Lambda表达式。
// arg0 is the first argument
(arg0, rest_of_args) -> arg0.methodName(rest_of_args)
// example, assume a and b are String
(a, b) -> a.compareToIgnoreCase(b)
方法参考。
// first argument type
arg0_Type::methodName
// arg0 is type of ClassName
ClassName::methodName
// example, a is type of String
String::compareToIgnoreCase
对于(String a, String b)
,其中a
和b
是任意名称,而String
是其任意类型。 本示例使用方法引用特定类型String
的任意对象a
(第一个参数)的实例方法compareToIgnoreCase
。
3.1查看此方法参考中的官方示例
String[] stringArray = { "Barbara", "James", "Mary", "John",
"Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
我们传递了一个方法参考String::compareToIgnoreCase
作为Arrays.sort
的比较器。
说明
查看Arrays.sort
方法签名:
public static void sort(T[] a, Comparator super T> c) {
}
在上面的示例中, Arrays.sort
需要Comparator
。 Comparator
是一个函数接口,其抽象方法compare
匹配BiFunction
,它接受String
两个参数并返回一个int
。
@FunctionalInterface
public interface Comparator {
int compare(T o1, T o2); // this matches BiFunction
}
查看BiFunction
方法签名:
@FunctionalInterface
public interface BiFunction {
R apply(T t, U u);
}
进一步阅读– Java 8 BiFunction示例
下面的lambda提供了BiFunction
,因此Arrays.sort
接受下面的lambda表达式作为有效语法。
(String a, String b) -> a.compareToIgnoreCase(b) // return int
// a is type of String
// method reference
String::compareToIgnoreCase
3.2让我们看看另一个例子。
package com.mkyong;
import java.util.function.BiPredicate;
import java.util.function.Function;
public class Java8MethodReference3a {
public static void main(String[] args) {
// lambda
int result = playOneArgument("mkyong", x -> x.length()); // 6
// method reference
int result2 = playOneArgument("mkyong", String::length); // 6
// lambda
Boolean result3 = playTwoArgument("mkyong", "y", (a, b) -> a.contains(b)); // true
// method reference
Boolean result4 = playTwoArgument("mkyong", "y", String::contains); // true
// lambda
Boolean result5 = playTwoArgument("mkyong", "1", (a, b) -> a.startsWith(b)); // false
// method reference
Boolean result6 = playTwoArgument("mkyong", "y", String::startsWith); // false
System.out.println(result6);
}
static R playOneArgument(String s1, Function func) {
return func.apply(s1);
}
static Boolean playTwoArgument(String s1, String s2, BiPredicate func) {
return func.test(s1, s2);
}
}
3.3让我们看看另一个示例,自定义对象。
package com.mkyong;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.function.BiFunction;
public class Java8MethodReference3b {
public static void main(String[] args) {
Invoice obj = new Invoice("A001", BigDecimal.valueOf(1.99), 3);
InvoiceCalculator formula = new InvoiceCalculator();
// lambda
BigDecimal result = calculate(formula, obj, (f, o) -> f.normal(o)); // 5.97
// method reference
BigDecimal result2 = calculate(formula, obj, InvoiceCalculator::normal); // 5.97
// lambda
BigDecimal result3 = calculate(formula, obj, (f, o) -> f.promotion(o)); // 5.37
// method reference
BigDecimal result4 = calculate(formula, obj, InvoiceCalculator::promotion); // 5.37
}
static BigDecimal calculate(InvoiceCalculator formula, Invoice s1,
BiFunction func) {
return func.apply(formula, s1);
}
}
class InvoiceCalculator {
public BigDecimal normal(Invoice obj) {
return obj.getUnitPrice().multiply(BigDecimal.valueOf(obj.qty));
}
public BigDecimal promotion(Invoice obj) {
return obj.getUnitPrice()
.multiply(BigDecimal.valueOf(obj.qty))
.multiply(BigDecimal.valueOf(0.9))
.setScale(2, RoundingMode.HALF_UP);
}
}
class Invoice {
String no;
BigDecimal unitPrice;
Integer qty;
// generated by IDE, setters, gettes, constructor, toString
}
第一个参数是InvoiceCalculator
的类型。 因此,我们可以引用特定类型InvoiceCalculator
的任意对象( f
)的实例方法( normal or promotion
)。
(f, o) -> f.normal(o))
(f, o) -> f.promotion(o))
InvoiceCalculator::normal
InvoiceCalculator::promotion
得到它了? 没有更多的例子
4.引用构造函数。
Lambda表达式。
(args) -> new ClassName(args)
方法参考。
ClassName::new
4.1引用默认构造函数。
package com.mkyong;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
public class Java8MethodReference4a {
public static void main(String[] args) {
// lambda
Supplier
4.2引用接受参数的构造方法– Invoice(BigDecimal unitPrice)
package com.mkyong;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
public class Java8MethodReference4b {
public static void main(String[] args) {
List list = Arrays.asList(
BigDecimal.valueOf(9.99),
BigDecimal.valueOf(2.99),
BigDecimal.valueOf(8.99));
// lambda
// List invoices = fakeInvoice(list, (price) -> new Invoice(price));
// method reference
List invoices = fakeInvoice(list, Invoice::new);
invoices.forEach(System.out::println);
}
static List fakeInvoice(List list, Function func) {
List result = new ArrayList<>();
for (BigDecimal amount : list) {
result.add(func.apply(amount));
}
return result;
}
}
class Invoice {
String no;
BigDecimal unitPrice;
Integer qty;
public Invoice(BigDecimal unitPrice) {
this.unitPrice = unitPrice;
}
//... generated by IDE
}
输出量
Invoice{no='null', unitPrice=9.99, qty=null}
Invoice{no='null', unitPrice=2.99, qty=null}
Invoice{no='null', unitPrice=8.99, qty=null}
做完了
参考文献
- Java 8方法参考:如何使用它
- 了解Java 8方法参考
- Lambda表达式的翻译
- Java教程–方法参考
- Java 8教程
- Java 8函数示例
- Java 8 BiFunction示例
- Java 8谓词示例
- Java 8 BiPredicate示例
- Java 8供应商示例
翻译自: https://mkyong.com/java8/java-8-method-references-double-colon-operator/