只包含一个抽象方法的接口,称为函数式接口,也称为SAM接口,即Single Abstract Method interfaces,用@FunctionalInterface注解进行标注,常见的函数式接口如Comparator,Runnable,Callable。
例子:
@FunctionalInterface
interface A {
public void test(); // 只能包含一个抽象方法
}
并且还可以使用泛型,如下:
@FunctionalInterface
interface A {
public void test(T t); // 只能包含一个抽象方法
}
函数式接口不仅仅只定义一个抽象方法,还可以定义
下面以 Comparator函数式接口为例:
这里的 equals(Object obj)方法来自于java.lang.Object里的public方法boolean equals(Object obj);
函数式编程(Functional Programming)是把函数作为基本运算单元,函数可以作为变量,可以接收函数,还可以返回函数。历史上研究函数式编程的理论是Lambda演算,所以我们经常把支持函数式编程的编码风格称为Lambda表达式,Java从JDK1.8开始支持这种风格 ( 针对函数式接口的一种简单写法
)。
(1) 使用Lambda表达式
例子:
@FunctionalInterface
interface A {
public void test(int a); // 只能包含一个抽象方法
}
class TestB {
public static void Test(Integer num, A a) {
a.test(num);
}
}
// 使用匿名内部类
TestB.Test(1, new A() {
@Override
public void test(int a) {
System.out.println("【方式1】实现A接口的test方法,num="+a);
}
});
在以前对于这种 函数式接口参数
我们使用匿名匿名内部类的方式传递变量过去,从Java 8开始,我们可以用Lambda表达式进行简化。
// 使用Lambda表达式
TestB.Test(2,(int a)->{
System.out.println("【方式2】实现A接口的test方法,num="+a);
});
运行效果:
(2) Lambda表达式的进一步简化
Lambda表达式在特殊情况下还可以进一步简化,如下:
未简化前:
// 使用Lambda表达式
TestB.Test(2,(int a)->{
System.out.println("【方式2】实现A接口的test方法,num="+a);
});
【简化1】参数的数据类型可以省略
TestB.Test(2,(a)->{
System.out.println("【方式2】实现A接口的test方法,num="+a);
}
);
【简化2】只有一个参数的时候可以省略()
TestB.Test(2,a->{
System.out.println("【方式2】实现A接口的test方法,num="+a);
}
);
【简化3】执行代码块中只有一条语句/只有一个return语句的时候可以省略{}
TestB.Test(2,a->
System.out.println("【方式2】实现A接口的test方法,num="+a)
);
(3) Java内置函数式接口
前文中的函数式接口都是我们自己定义的,其实Java官方给我提供了一些函数式接口,我们可以直接使用这些接口,而无需自己去定义函数式接口。
上文例子中的函数式接口对照上表来看的话,和消费性接口相对应,这里我们采用Consumer接口来试试看:
定义TestB_2类,采用 Consumer
函数式接口:
class TestB_2 {
public static void Test(Integer num, Consumer c) {
c.accept(num);
}
}
使用Lambda表达式进行调用:
TestB_2.Test(3,(a)-> System.out.println("【方式3】实现A接口的test方法,num="+a));
运行效果:
其实还可以进一步简化:
BiConsumer consumer = (a, b) -> System.out.println(a+""+b);
consumer.accept("【方式4】实现A接口的test方法,num=",4);
运行效果:
(1) 方法引用的简单使用
方法引用(Method References)是一种语法糖,它本质上就是 Lambda 表达式,我们知道Lambda表达式是函数式接口的实例,所以说方法引用也是函数式接口的实例,通过 类/对象::方法
进行引用。
例子:
@FunctionalInterface
interface A {
public void test(int a); // 只能包含一个抽象方法
}
class TestB {
public static void Test(String[] arrays, A a) {
for (int i = 0; i < arrays.length; i++) {
System.out.println(arrays[i]);
}
}
}
在前文中我们是通过Lambda表达式进行简化:
// 使用Lambda表达式
TestB.Test(2,(int a)->{
System.out.println("【方式2】实现A接口的test方法,num="+a);
});
当存在如下类时,C类下的静态方法testC(int a)和函数式接口方法 test(int a);的形参和返回值类型保持一致时,就可以通过方法引用进行简化,如下:
class C{
public static void testC(int a){
System.out.println("【方式5】实现A接口的test方法,num="+a);
}
}
使用方法引用进行简化:
// 使用引用
TestB.Test(5,C::testC);
运行效果:
(2) 方法引用的分类
对于方法引用的使用,通常可以分为以下 4 种情况:
分清楚实例方法,静态方法和构造方法
下面的equals(Object o)方法就是实例方法:
public final class Integer {
boolean equals(Object o) {
...
}
}
下面的parseInt(String s)方法就是静态方法:
public final class Integer {
public static int parseInt(String s) {
...
}
}
下面的方法就是构造方法:
public class A{
public A(){
}
}
情况1( 对象 :: 非静态方法
):
例子:
Consumer consumer = System.out::println;
consumer.accept("【情况1】( 对象 :: 非静态方法 )方法引用");
对应的Lambda表达式如下:
Consumer consumer2 = m -> System.out.println(m);
运行效果:
System.out为对象:
println为实例方法:
情况2( 类 :: 静态方法
):
例子:
Comparator cm= Integer::compare;
System.out.println("【情况2】( 对象 :: 静态方法 )方法引用,100和99做比较:"+cm.compare(100, 99));
Lambda表达式如下:
Comparator cm2 = (x, y) -> Integer.compare(x, y);
运行效果:
Integer为类:
compare为静态方法:
情况3( 类 :: 非静态方法
):
例子:
Comparator cmStr = String::compareTo;
System.out.println("【情况3】( 对象 :: 非静态方法 )方法引用,字符串a和b做比较:"+cmStr.compare("a", "b"));
Lambda表达式如下:
Comparator cmStr2 = (s1, s2) -> s1.compareTo(s2);
运行效果:
String为类:
compareTo为实例方法:
分清楚compareTo()方法和compare()方法的区别:
备注:compareTo()方法和compare()方法的区别更加详细解释,请参照这篇博文: java中compareTo和compare方法之比较
根据上面的理解我们来分析一下"a"和"b"是怎么进行比较的:
根据上文中二者的区别,可知使用 String::compareTo
表示采用String的排序操作,而 cmStr.compare("a", "b")
表示调用 "a"
的 compareTo("b")
进行比较。
如下图所示:
"a"
从哪里来的呢?,其实这个 "a"
就是this本身,而 this.value = 'a'
:
情况4( 类 :: new
):
例子:
class Student{
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
方法引用:
Function function = Student::new;
Student zs = function.apply("张三");
System.out.println("【情况4】( 类 :: new )方法引用,获取学生姓名:"+zs.getName());
Lambda表达式如下:
Function function2 = (name) -> new Student(name);
运行效果:
Student为类:
Function
这里的String对应Student的构造方法的参数类型
对上文的说法进行验证( 确实如此
):