参考教材:《Java 核心技术 卷一》
前言
在JAVA SE 8 之前,在java中传递一个代码块并不容易,不能直接传递代码块,因为java是一种面向对象的语言,所以想传递代码块需要把所需传递的代码写在类的方法里,然后通过传递类的实例(即对象)才能达到传递代码块的目的。
为了解决这一问题,JAVA SE 8 加入lambda表达式
这一新特性,可以用一种简洁的语法来定义代码块,并把这个代码块传递给某个对象,这个代码块可以在将来的某个时间被调用一次或者多次调用。
语法
lambda表达式的一般构成:参数, 箭头(->) 以及一个表达式
如果代码要完成的计算无法放在一个表达式中,就可以像写方法一样,把这些代码放在 {}里,还可以显式的使用return语句。
即:(parameters) -> expression
或 (parameters) ->{ statements; }
实例:
//参数可以为空,但仍然需要提供括号
()-> System.out.println("haha");
//多行代码时需要用{}包裹,还可以使用return语句
(int x)->{ System.out.println(x);return x*x;}
//可以不指定的返回类型。返回类型总是会由上下文推导得出
(String first,String second)->first.length()-second.length();
//当参数类型可以通过上下文推导出来,参数类型可以省略
//比如下面,lambda表达式被赋值给一个字符串比较器,所以编译器可以推导出
//参数类型是String
Comparator<String> comp = (first,second)->first.length()-second.length();
//当参数只有一个,且类型可以由上下文推导而出的时候,小括号可以省略
ActionListener listener = event -> System.out.println("现在的时间为:"+new Date());
函数式接口的编写
java已经提供了支持lambda表达式的接口,比如上面的ActionListener
,Comparator
,那么问题来了,接口符合什么要求就可以支持lambda表达式呢?
要求很简单:接口有且最多只能有一个抽象方法
(ps:可以有其他默认方法,但抽象方法只能有一个,否则lambda表达式赋值的时候会因为无法确定是哪个抽象方法而报错)。
现在我们就试着自己编写一个函数式接口,并试着给他传入一个lambda表达式,看能不能正常接收。
public interface Person{
public void test(String s);
}
好了,这样就写好了一个支持lambda表达式的函数式接口。现在是不是发现这个接口没有任何特殊之处,就像我们平时写的一些接口。(换句话说,你平时写的一些接口就支持lambda表达式)。
下面我们就编写一个lambda表达式传入test方法。
public class PersonTest {
public static void main(String[] args){
//向p对象中的test方法传入lambda表达式
//下面两个表达式等价
//因为参数只有一个,且参数类型可从test方法的定义推出
//所以可以省略小括号和参数类型
Person p = s -> System.out.println(s);
//Person p = (String s)-> System.out.println(s);
//调用p对象的test方法时,会执行刚刚传入的lambda表达式
p.test("lambda表达式测试");
}
}
执行结果如下:
重新写下Person接口再测试一下
public interface Person{
//把刚刚的抽象方法改成默认方法
public default void test(String s){
System.out.println("这是接口的默认方法,传入的字符串为:"+s);
};
//重新定义一个有返回值抽象的方法
public int factorial(int i);
}
public class PersonTest {
public static void main(String[] args){
Person p = i -> {
//返回i的阶乘
int sum=1;
while (i>0){
sum*=i;
i--;
}
return sum;
};
//调用默认方法
p.test("哈哈哈");
//调用抽象方法
int sum = p.factorial(5);
System.out.println("5的阶乘为"+sum);
}
}
运行结果
lambda表达式的用途及实例
使用lambda表示式的特点是延迟执行
,毕竟如果是想立即执行代码的话,完全可以直接执行,不用把它放到lambda表达式里。(所以我上面写的例子可能体现不出lambda表达式的特点,只是为了让读者了解lambda表达式的编写及调用)
那哪些情况需要代码延迟执行呢
? ,比如下列情况
- 在一个单独的线程中运行代码;
- 多次运行代码;
- 在算法的适当位置运行代码(例如, 排序中的比较操作);
- 发生某种情况时执行代码(如, 点击了一个按钮, 数据到达, 等等)
- 只在必要时才运行代码。
示例一:构造线程
//使用匿名类方式构建线程
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hahahaha");
}
});
thread.start();
//使用lambda表达式
Thread thread = new Thread(()-> System.out.println("hahahaha"));
thread.start();
示例二:列表迭代
List<String> languages = Arrays.asList("Java","C++","Python","JavaScript","C#");
//一般的迭代遍历方式
for (String language:languages){
System.out.println(language);
}
//使用lambda表达式
languages.forEach(n-> System.out.println(n));
示例三:数组排序
String[] languages = new String[]{"Java","C++","Python","JavaScript","C#"};
//使用匿名类方式
Arrays.sort(languages, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
//根据长度排序
return o1.length()-o1.length();
}
});
//使用lambda形式
Arrays.sort(languages,(first,second)->first.length()-second.length());