在Java 8中,双冒号(::)运算符称为方法引用。请参考以下示例:
匿名类打印列表。
List<String> list = Arrays.asList("node", "java", "python", "ruby");
list.forEach(new Consumer<String>() { // anonymous class
@Override
public void accept(String str) {
System.out.println(str);
}
});
匿名类-> Lambda表达式。
List<String> list = Arrays.asList("node", "java", "python", "ruby");
list.forEach(str -> System.out.println(str)); // lambda
Lambda表达式->方法引用。
List<String> 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。
Java8MethodReference1a.java
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<String> list = Arrays.asList("A", "B", "C");
// anonymous class
list.forEach(new Consumer<String>() {
@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此示例将String列表转换为Integers列表,该方法引用静态方法Integer::parseInt。
整数.java
public static int parseInt(String s) throws NumberFormatException {
return parseInt(s,10);
}
Java8MethodReference1b.java
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<String> list = Arrays.asList("1", "2", "3");
// anonymous class
List<Integer> collect1 = list.stream()
.map(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s);
}
})
.collect(Collectors.toList());
// lambda expression
List<Integer> collect2 = list.stream()
.map(s -> Integer.parseInt(s))
.collect(Collectors.toList());
// method reference
List<Integer> collect3 = list.stream()
.map(Integer::parseInt)
.collect(Collectors.toList());
}
}
1.3此示例将两个连接Integer并返回String。它将方法引用静态方法IntegerUtils::join作为参数传递给另一个接受的方法BiFunction。
Java8MethodReference1c.java
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<Integer, Integer, String>() {
@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> R playTwoArgument(Integer i1, Integer i2,
BiFunction<Integer, Integer, R> 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按薪水排序列表。我们可以参考compareBySalary特定对象的实例方法ComparatorProvider。
Java8MethodReference2
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<Employee> 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<Employee>() {
@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());
}
}
Employee.java
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是其任意类型。此示例使用方法引用来引用特定类型compareToIgnoreCase的任意对象a(第一个参数)的实例方法String。
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 <T> void sort(T[] a, Comparator<? super T> c) {
}
在以上示例中,Arrays.sort期望为Comparator<String>。的Comparator是一个功能接口,其抽象方法compare匹配BiFunction<String, String, Integer>,它需要的两个参数,String并返回一个int。
比较器
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2); // this matches BiFunction<String, String, Integer>
}
查看BiFunction方法签名:
BiFunction.java
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
}
进一步阅读– Java 8 BiFunction示例
下面的lambda提供了的实现BiFunction<String,String,Integer>,因此Arrays.sortaccept接受下面的lambda表达式作为有效语法。
(String a, String b) -> a.compareToIgnoreCase(b) // return int
// a is type of String
// method reference
String::compareToIgnoreCase
3.2让我们看另一个例子。
Java8MethodReference3a.java
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> R playOneArgument(String s1, Function<String, R> func) {
return func.apply(s1);
}
static Boolean playTwoArgument(String s1, String s2, BiPredicate<String, String> func) {
return func.test(s1, s2);
}
}
3.3让我们看看另一个示例,自定义对象。
Java8MethodReference3b.java
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<InvoiceCalculator, Invoice, BigDecimal> 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。因此,我们可以引用特定类型normal or promotion的任意对象(f)的实例方法()InvoiceCalculator。
(f, o) -> f.normal(o))
(f, o) -> f.promotion(o))
InvoiceCalculator::normal
InvoiceCalculator::promotion
知道了?没有更多的例子🙂
4.引用构造函数。
Lambda表达式。
(args) -> new ClassName(args)
方法参考。
ClassName::new
4.1引用默认构造函数。
Java8MethodReference4a.java
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<Map> obj1 = () -> new HashMap(); // default HashMap() constructor
Map map1 = obj1.get();
// method reference
Supplier<Map> obj2 = HashMap::new;
Map map2 = obj2.get();
// lambda
Supplier<Invoice> obj3 = () -> new Invoice(); // default Invoice() constructor
Invoice invoice1 = obj3.get();
// method reference
Supplier<Invoice> obj4 = Invoice::new;
Invoice invoice2 = obj4.get();
}
}
class Invoice {
String no;
BigDecimal unitPrice;
Integer qty;
public Invoice() {
}
//... generated by IDE
}
4.2引用接受参数的构造函数– Invoice(BigDecimal unitPrice)
Java8MethodReference4b.java
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<BigDecimal> list = Arrays.asList(
BigDecimal.valueOf(9.99),
BigDecimal.valueOf(2.99),
BigDecimal.valueOf(8.99));
// lambda
// List<Invoice> invoices = fakeInvoice(list, (price) -> new Invoice(price));
// method reference
List<Invoice> invoices = fakeInvoice(list, Invoice::new);
invoices.forEach(System.out::println);
}
static List<Invoice> fakeInvoice(List<BigDecimal> list, Function<BigDecimal, Invoice> func) {
List<Invoice> 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}
完毕。