前言:
接上一篇文章:Java8新特性(内置的核心函数式接口)http://blog.csdn.net/xinghuo0007/article/details/78603908
我们知道使用了Lambda表达式以后能够是我们的代码的精简,而且可读性更强,但是有时Lambda表达式的Lambda体内的功能我们已经实现过了,
这个时候就可以使用我们的函数引用、构造器引用了
知识点一:方法引用
什么是方法引用?
答:方法引用是对Lambda表达式符合某种情况下的一种缩写,使得我们的Lambda表达式更加的精简,
也可以理解为Lambda表达式的另一种表现形式(缩写)
什么时候使用方法引用呢?
答:当要传递给Lambda体内的操作,已经有实现的方法了,就可以使用方法引用了
方法引用使用的前提条件是什么呢?
答:
1.方法引用所引用的方法的参数列表必须要和函数式接口中抽象方法的参数列表相同(完全一致)
2.方法引用所引用的方法的的返回值必须要和函数式接口中抽象方法的返回值相同(完全一致)
方法引用有什么语法格式吗?
方法引用一般有三种格式:
1. 实例对象名::实例方法名
2. 类名::静态方法名
3. 类名::实例方法名 (注意区别2,3的区别,下面会说)
2,3的区别:
若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: 类名::实例方法名
方法引用举例:
举例一:1. 实例对象名::实例方法名(案例一)
Student student = new Student("XiangYang",23);
Supplier<String> supplier = ()->student.getName();
System.out.println("Lambda形式: "+supplier.get());
Supplier<String> supplier1 = student :: getName;
System.out.println("方法引用形式: "+supplier1.get());
class Student{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name) {
this.name = name;
}
public Student(int age) {
this.age = age;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
举例一: 1. 实例对象名::实例方法名 (案例二)
@org.junit.Test
public void test6(){
//传统Lambda表达式
Consumer<String> consumer = (x) -> System.out.println(x);
consumer.accept("Hi: 我是Lambda表达式实现的!"); //打印:Hi: 我是Lambda表达式实现的!
//方法引用实现
consumer = System.out::println;
consumer.accept("Hello : XiangYang,我是使用方法引用实现的 ");//打印:Hello : XiangYang,我是使用方法引用实现的
}
举例二: 2. 类名::静态方法名
public class Test{
@org.junit.Test
public void test8(){
//传统Lambda表达式
Consumer<String> consumer = (str) -> sop(str);
consumer.accept("Hello : XiangYang"); //打印:Hello : XiangYang
//方法引用方式
consumer = Test::sop;
consumer.accept("Hello : XiangYang"); //打印:Hello : XiangYang
}
}
举例三: 3. 类名::实例方法名
@org.junit.Test
public void test7(){
//传统Lambda表达式
BiPredicate<String,String> biPredicate = (x,y) -> x.equals(y);
boolean test = biPredicate.test("hello", "hi");
System.out.println(test);//false
//方法引用
biPredicate = String::equals;
test = biPredicate.test("hello", "hello");
System.out.println(test);//true
}
注意:
1.这里一定要知道 类名::实例方法名 这种语法的使用条件:
说明:a,b是所有方法引用的必要条件,c是使用 类名::实例方法名的特殊前提
a.方法引用所引用的方法的参数列表必须要和函数式接口中抽象方法的参数列表相同(完全一致)
b.方法引用所引用的方法的的返回值必须要和函数式接口中抽象方法的返回值相同(完全一致)
c.若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: 类名::实例方法名
2.解释一下:
首先看一下使用传统Lambda表达式的形式:
//传统Lambda表达式
BiPredicate<String,String> biPredicate = (x,y) -> x.equals(y);
boolean test = biPredicate.test("hello", "hi");
System.out.println(test);//false
注意看: equals方法的调用者是x,equals方法参数是y,这符合我们上面的c条件;所以能使用
方法引用总结:
1.一定弄明白使用方法引用的前提条件(2个)
a. 方法引用所引用的方法的参数列表必须要和函数式接口中抽象方法的参数列表相同(完全一致)
b .方法引用所引用的方法的的返回值必须要和函数式接口中抽象方法的返回值相同(完全一致)
这里使用代码解释一下:
@org.junit.Test
public void test6(){
//传统Lambda表达式
Consumer<String> consumer = (x) -> System.out.println(x);
consumer.accept("Hi: 我是Lambda表达式实现的!"); //打印:Hi: 我是Lambda表达式实现的!
//方法引用实现
consumer = System.out::println;
consumer.accept("Hello : XiangYang,我是使用方法引用实现的 ");//打印:Hello : XiangYang,我是使用方法引用实现的
}
解释:
1.首先我们看一下Consumer的函数式接口的源码的接口
void accept(T t);
由于我们给泛型传入的是String类型
Consumer<String> consumer = (x) -> System.out.println(x);
所以此时
//这是函数式接口中抽象方法,接收了实际类型String,没有返回值
void accept(T t); ==》 void accept(String t);
2 我们在看我们的Lambda体内的实现
System.out.println(x);
此时,我们查看println方法的源码:
//这是 方法引用所引用的方法 的参数类型是String,无返回值
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
3. 此时函数式的接口中的参数类型与返回值 和 方法引用所引用方法的参数类型与返回值相同,所以可以使用方法引用,类型为: 实例对象名:: 实例方法名
知识点二:构造器引用
什么是构造器引用?
答:与函数式接口相结合,自动与函数式接口中方法兼容。
可以把构造器引用赋值给定义的方法。
什么时候用构造器引用?
答:在使用Lambda表达的时,满足使用构造器引用条件的时候可以使用
构造器使用的前提是什么?
答:构造器参数列表要与接口中抽象方法的参数列表一致!
构造器引用的语法格式是什么?
语法格式:
类名 :: new
举例:
说明:Student类见方法引用举例一的案例
@org.junit.Test
public void test9(){
//传统Lambda方式
Supplier<Student> studentSupplier = ()-> new Student();
Student student = studentSupplier.get();
System.out.println(student);//Student{name='null', age=0}
//构造器引用
studentSupplier = Student::new;
student = studentSupplier.get();
System.out.println(student);//Student{name='null', age=0}
}
案例说明:
1.首先我们要只是Supplier接口的抽象方法定义为:
T get();
我们代码
Supplier<Student>此时已经泛型T传入Student类型
相当于:
Student get(); //函数式接口中是无参,返回值为Student
2.在看我们的Student类的定义中一个无参构成器
Student(){} //构造器无参,返回Student类型(严格意义上两个构成函数没有返回值,我们先这样理解)
3.此时,符合我们使用构造方法引用的前提:
构造器参数列表要与接口中抽象方法的参数列表一致!
所以:可以使用构造引用。
知识点三:数组引用
说明:
这里单独把数组引用拿出来实际上是强调一下,数组引用和构造引用基本相同,直接上一个案例来说明问题
案例:
@org.junit.Test
public void test10(){
//需求:获取n个int[] 数组
//传统Lambda实现
Function<Integer,int[]> function = (i) -> new int[i];
int[] apply = function.apply(5);
System.out.println(apply.length); // 5
//数组类型引用实现
function = int[] ::new;
apply = function.apply(10);
System.out.println(apply.length); // 10
}