方法引用是什么? 方法引用是java8的新特性之一, 可以直接引用已有Java类或对象的方法或构造器。方法引用与lambda表达式 结合使用,可以进一步简化代码。
1 2 3 4 5 6 7 8 public class TestMethodReference { public static void main (String[] args) { List<String> strList = Arrays.asList(new String[] { "a" , "c" , "b" }); strList.stream().sorted((s1, s2) -> s1.compareToIgnoreCase(s2)).forEach(s -> System.out.println(s)); strList.stream().sorted(String::compareToIgnoreCase).forEach(System.out::println); } }
方法引用的具体使用 java8方法引用有四种形式:
静态方法引用 : ClassName :: staticMethodName
构造器引用 : ClassName :: new
类的任意对象的实例方法引用: ClassName :: instanceMethodName
特定对象的实例方法引用 : object :: instanceMethodName
lambda表达式可用方法引用代替的场景 可以简要概括为:lambda表达式的主体仅包含一个表达式,且该表达式仅调用了一个已经存在的方法 。方法引用的通用特性 :方法引用所使用方法的入参和返回值与lambda表达式实现的函数式接口的入参和返回值一致
静态方法引用 静态方法引用的语法格式为: 类名::静态方法名 ,如 System.out::println 等价于lambda表达式 s -> System.out.println(s) ,代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class TestMethodReference { public static void main (String[] args) { Arrays.asList(new String[] {"小王" , "小赵" , "小壮" }).stream().forEach(s -> TestMethodReference.HelloWorld(s)); Arrays.asList(new String[] {"小王" , "小赵" , "小壮" }).stream().forEach(TestMethodReference::HelloWorld); } public static void HelloWorld (String s) { System.out.println("Hello World," + s); } }
静态方法引用适用于lambda表达式主体中仅仅调用了某个类的静态方法的情形 。
构造器引用 **构造器引用的语法格式为: 类名::new ,如
1 () -> new ArrayList <String>() 等价于 ArrayList <String>::new
,代码示例:
1 2 3 4 5 Supplier<List<String>> supplier1= () -> new ArrayList<String>(); Supplier<List<String>> supplier = ArrayList<String>::new ;
构造器引用适用于lambda表达式主体中仅仅调用了某个类的构造函数返回实例的场景。
类的任意对象的实例方法引用 类的任意对象的实例方法引用的语法格式为: 类名::实例方法名 , 这种方法引用相对比较复杂,我们来看示例:
上述示例中,strs为一个String数组,lambda表达式(s1,s2)->s1.compareToIgnoreCase(s2)实现函数式接口的是Comparator接口, 我们看下jdk8中Comparator接口的源码(截取部分):
1 2 3 4 @FunctionalInterface public interface Comparator <T > { int compare (T o1, T o2) ; }
而String类的compareToIgnoreCase方法源码为:
1 2 3 public int compareToIgnoreCase (String str) { return CASE_INSENSITIVE_ORDER.compare (this , str); }
可以发现函数式接口Comparator《String》的compare方法比String类的compareToIgnoreCase方法多了一个String类型的入参。看到这里对类的任意对象的实例方法引用的使用可能似懂非懂,下面我们看一个自己实现一个类的任意对象的实例方法引用的示例(示例2)。
二、示例2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class Student { private String name; private Integer score; public void setNameAndScore (String name, Integer score) { this .name = name; this .score = score; System.out.println("Student " + name +"'s score is " + score); } public static void main (String[] args) { TestInterface testInterface = Student::setNameAndScore; testInterface.set(new Student(), "DoubleBin" , 100 ); } @FunctionalInterface interface TestInterface { public void set (Student d, String name, Integer score) ; } }
看完上述代码,我们可以总结出类的任意对象的实例方法引用的特性 为:
1、方法引用的通用特性 :方法引用所使用方法的入参和返回值与lambda表达式实现的函数式接口的入参和返回值一致;
2、lambda表达式的第一个入参为实例方法的调用者,后面的入参与实例方法的入参一致 。
特定对象的实例方法引用 特定对象的实例方法引用的语法格式为: 对象::实例方法名 , 示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Test { public static void main(String[] args) { Test test = new Test(); // lambda表达式使用: Arrays.asList(new String[] {"a", "c", "b"}).stream().forEach(s -> test.println(s)); // 特定对象的实例方法引用: Arrays.asList(new String[] {"a", "c", "b"}).stream().forEach(test::println); } public void println(String s) { System.out.println(s); } }
特定对象的实例方法引用适用于lambda表达式的主体中仅仅调用了某个对象的某个实例方法的场景 。
总结 方法引用使用运算符::连接类(或对象)与方法名称(或new)实现在特定场景下lambda表达式的简化表示,使用时要注意方法引用的使用场景及各种方法引用的特性。使用方法引用的好处是能够更进一步简化代码编写,使代码更简洁。 方法引用代替lambda表达式对代码的简化程度远远没有lambda表达式代替匿名类的简化程度大, 有时反而增加了代码的理解难度(如 类的任意对象的实例方法引用),且使用场景的局限性不利于增加或修改代码,个人认为有时没有必要刻意使用方法引用