函数式接口
函数式编程的最大优点是不会发生死锁,因为没有变量,都是语句。所以务必重视接下来的函数式接口的学习
函数式接口:有且仅有一个抽象方法的接口
Lambda表达式的前提:必须要有接口,并且要求接口中有且仅有一个抽象方法
Java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口,只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利进行推导
简单理解就是:函数式接口是适用于Lambda表达式的接口
函数式接口可以用作方法的参数传递、局部变量
如何检测一个接口是不是函数式接口呢,使用注解@FunctionalInterface即可,如下 1、@FunctionalInterface 2、放在接口定义的上方:如果接口是函数式接口,编译通过;如果不是,编译失败
注意:我们自己定义函数式接口的时候,@FunctionalInterface是可选的,就算我不写这个注释,只要保证满足函数式接口定义的条件,也照样是函数式接口。但是,建议加上该注解
函数式接口的练习
xxxxxxxxxx
package ch25;
//函数式接口:有且仅有一个抽象方法的接口
//Java提供的函数式表达式的注解,相当于前面学的'@Override'重写方法的注解。如果确定是函数是接口,就自己写上这个注解
public interface a_1_1MyInterface {
//无参无返回值的抽象方法
void show();
//最上面使用了注解之后,就代表这个接口必须是函数式接口。如果在下面写多个抽象方法,那么就会报错
/*void show2();
void show3();
void show4();*/
//注意不写注解也是可以的。但是为了让该接口不能被继续写更多的抽象方法进来,还是建议把注解写上
}
xxxxxxxxxx
package ch25;
public class a_1_2测试 {
public static void main(String[] args) {
//函数式接口可以用作方法的参数传递,用作局部变量
//当把'a_1_1MyInterface'接口用作局部变量my时,可以直接把Lambda表达式赋值给该局部变量my,如下
a_1_1MyInterface my = ()-> System.out.println("函数式接口-局部变量");
my.show();
}
}
函数式接口_作为方法的参数
需求: 定义一个测试类,在测试类里面定义一个方法startThread(Runnable r),startThread方法的参数是一个函数式接口, 最后在测试类里面的main方法里面调用startThread方法
如果方法的参数是一个函数式接口,我们可以使用Lambda作为参数传递
函数式接口_作为方法参数的练习
x
package ch25;
public class a_2_1测试 {
public static void main(String[] args) {
//使用匿名内部类的方式来调用最下面的startThread方法
startThread(new Runnable() { //
public void run() {
System.out.println(Thread.currentThread().getName()+"线程启动了");//currentThread().getName()获取线程名
System.out.println("-------------------");
}
});//startThread括号里面形参需要的是Runnable接口的实现类对象,我们写上new Runnable(),再回车,就能生成现在的代码样子
//--------------------------------------------------------------------------------------------------------
//使用Lambda表达式的方式来调用最下面的startThread方法
startThread(()-> System.out.println(Thread.currentThread().getName()+"线程启动了"));
//思考:为什么什么那行箭头前面的小括号没有写参数呢?
//原因:Runnable接口里面的run方法也没有形参,所以这里不需要传参数
//深度理解:最下面的startThread方法的形参是函数式接口,我们可以直接把上面的Lambda表达式作为参数传递给startThread方法
//即()-> System.out.println(Thread.currentThread().getName()+"线程启动了")作为参数传递
}
//选中Runnable,按Ctrl+B查看源码,可以发现Runnable接口实际上是一个函数式接口,它里面只有一个run抽象方法
private static void startThread(Runnable r){
//Thread t = new Thread(r);
//t.start();
//上面两行可以优化合并为一行
new Thread(r).start();//作用是开启一个线程
}
}
函数式接口_作为方法的返回值
需求:
定义一个测试类,在测试类里面定义一个方法Comparator
如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回
函数式接口_作为方法返回值的练习
xxxxxxxxxx
package ch25;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
public class a_3_1测试 {
public static void main(String[] args) {
//如何使用最下面的getComparator方法。我们需要先构造一个使用场景
//定义一个集合存储字符串元素
ArrayList<String> array = new ArrayList<>();
//存储字符串元素
array.add("cccc");
array.add("aa");
array.add("b");
array.add("ddd");
//对集合中的字符串进行排序之前
System.out.println("排序前:"+array);
//对集合中的字符串进行排序,下面那行的参数只传集合进去,则按照自然顺序进行排序
Collections.sort(array);
System.out.println("Collections集合的sort方法排序:"+array);
System.out.println("---------------------------------------");
//如何使用指定的比较器排序,注意我们在比较器里面使用两种了方式实现getComparator方法
//第二个参数需要的是接口的实现类对象,刚好我们最下面的getComparator方法返回的就是接口的实现类对象
Collections.sort(array,getComparator());
System.out.println("比较器排序:"+array);//排序的依据不是按字母顺序了,而是要看getComparator方法里面的具体代码
}
//---------------------------------------------------------------------------------------------------------------
private static Comparator<String> getComparator(){//getComparator方法的返回值类型是Comparator<String>接口
//上面那行的Comparator接口是函数式接口,Java提供的接口。可选中Comparator,按Ctrl+B,可发现Comparator接口里面有@FunctionalInterface注解
//Comparator是一个比较器排序的接口
//getComparator方法返回值是Comparator接口,实际上getComparator方法返回的是Comparator接口的实现类对象
/*//使用匿名内部类的方式实现getComparator方法
*//*Comparator<String> comp = new Comparator<String>() {//输入Comparator<String> comp = new Comparator再回车就生成下面的代码
@Override
public int compare(String s1, String s2) {
return s1.length()-s2.length();
}
};
return comp;*//*
//上面那行return的是comp,而comp最终还是会new一次Comparator<String>(){},所以我们可以优化一下,即17行左边不接收,直接返回
//先把上面的代码注释起来,不然下面的优化写法会冲突报错。优化写法如下
return new Comparator<String>() {//输入Comparator<String> comp = new Comparator再回车就生成下面的代码
@Override
public int compare(String s1, String s2) {//有两个String类型的参数
return s1.length()-s2.length();//按照字符长度进行从短到长排序
}
};*/
//-------------------------------------------------------------------------------------------------------
//使用Lambda表达式的方式实现getComparator方法。直接根据54行的代码进行改编哦,毕竟那里是优化后的
//注意,使用这里的Lambda表达式的方式,需要先把匿名内部类的方式都注释掉,不然会冲突报错。简单来说就是匿名内部类和Lambda只能使用其中一个
/*return (String s1,String s2)->{
return s1.length()-s2.length();
};*/
//优化一下上面的Lambda表达式,如下
return (s1,s2)->s1.length()-s2.length();
//总结:如果方法的返回值是函数式接口,即这里的getComparator方法的返回值是Comparator接口,那么我们可以把Lambda表达式作为
//结果,return出去。在开发中非常好用,不信你看看上面那些没被注释的代码,一点点代码就把getComparator方法写好了
//怎么理解上面从38行开始这么多被注释掉的代码呢?如下
//我们先是采用了匿名内部类的写法进行演示了一遍,又使用匿名内部类的优化写法演示了一遍。再使用Lambda表达式的写法演示了一遍,
//又使用Lambda表达式的优化写法演示了一遍。所以代码量就多了,最后我们采用的是Lambda表达式的优化写法。也反映出以后在开发中如果
//碰到当一个方法的返回值是函数式接口的时候,我们可以使用Lambda表达式直接return出去,避免了写大量的代码
}
}