目录
为什么引入lambda表达式?
到目前为止(java7之前),在Java中传递一个代码段并不方便,你不能直接传递代码段。因为Java是一种面向对象的语言,所以必须创建一个对象,由这个对象实现所需方法片段》。
设计者们做了多年尝试,终于找到了一种适合Java的设计——lambda表达式。
一、定义
《Java核心技术 I》里是这么定义的:
lambda表达式是一个可传递的代码块,可以在以后一次或多次执行,它采用了一种简洁的方式来定义代码块。
1、传统方式调用
//接口实现
public class Runnable1 implements Runnable {
@Override
public void run() {
System.out.println("执行了多线程方法!");
}
}
public class MyLambda1 {
public static void main(String[] args) {
Runnable1 lambda1=new Runnable1();
Thread thread=new Thread(lambda1);
thread.run();
}
}
2、匿名内部类
public class MyLambda1 {
public static void main(String[] args) {
Runnable lambda2 = new Runnable() {
@Override
public void run() {
System.out.println("执行了多线程方法!");
}
};
Thread thread=new Thread(lambda2);
thread.run();
}
}
3、lambda表达式
public class MyLambda1 {
public static void main(String[] args) {
//3、lambda表达式
Thread thread=new Thread(()->{
System.out.println("执行了多线程方法!");
});
thread.run();
}
}
可能会有同学要问了,匿名内部类跟lambda表达式感觉没多大区别啊!能不能不学啊!
lambda表达式确实写法简洁了,而且实现方式是不同(后面会比较二者的不同)。
二、lambda表达式的语法
Lambda表达式的语法格式:
(参数列表 ) - > { 语句或语句块 }
- ( ):若为空,则接口方法没有参数
- - >:箭头指向{}中的方法代码块
- { }:包含一段代码块,即方法体中的内容
【例子展示】
1、无参数无返回值
public interface Cat {
void say();
}
public class Lambda2 {
public static void main(String[] args) {
testCat(() -> {
System.out.println("猫叫声:喵!喵!喵!");
});
}
public static void testCat(Cat cat) {
cat.say();
}
}
2、带参数无返回值
public interface Dog {
void call(String msg);
}
public class Lambda2 {
public static void main(String[] args) {
testDog((String msg) -> {
System.out.println("狗叫声:" + msg);
});
}
public static void testDog(Dog dog) {
dog.call("汪!汪!汪!");
}
}
3、带参数有返回值
public interface Operation {
int add(int n, int m);
}
public class Lambda2 {
public static void main(String[] args) {
testOperation((int n,int m) -> {
return n+m;
});
}
public static void testOperation(Operation operation) {
System.out.println(operation.add(10, 20));
}
}
【 写法简化】:
- 单个参数可省略()
- 参数类型可以省略(必须同时省略或者不省略)
- 方法体单语句可以省略 { }
/省略参数括号、方法体括号
testDog(msg -> System.out.println("狗叫声:" + msg));
//方法引用
//打印对象是传入参数
testDog(System.out::println);
三、常见的lambda表达式使用场景
在《JAVA核心技术 I》中这样描述:
使用 lambda表达式的重点是延迟执行(deferred execution)毕竟,如果想要立即执行代
码,完全可以直接执行,而无须把它包装在一个 lambda表达式中。之所以希望以后再执行
代码,这有很多原因,如:
- 在一个单独的线程中运行代码;
- 多次运行代码;
- 在算法的适当位置运行代码(例如,排序中的比较操作);
- 发生某种情况时执行代码(如,点击了一个按钮,数据到达,等等);
- 只在必要时才运行代码;
1、重复执行代码:
repeat(10, () -> System.out.println("Hello,World!"));
public static void repeat(int n, Runnable action) {
for (int i = 0; i < n; i++) {
action.run();
}
}
2、延迟执行(必要时才运行、发生某种情况时执行代码)
javaFX中给按钮绑定事件,某一事件才触发按钮
Button button = new Button("按钮");
button.setOnAction(event -> {
//do something
});
3、在算法的适当位置运行代码(例如,排序中的比较操作)
在compartor的比较方法执行结束前,不会进行排序。
//自定义数组
String [] arg={"acc","ddd","aaa","bbb","ccc"};
Arrays.sort(arg,(first,second)->{
return first.length()-second.length();
});
四、再提经典使用
1、Java的四大函数式接口
a、Consumer
表示接受单个输入参数且不返回结果的操作。 与大多数其他功能界面不同,Consumer被期望通过副作用来操作。
public class ConsumerTest1 {
public static void main(String[] args) {
String [] arr={"花木兰,21","小乔,23","孙尚香,22"};
consumer(arr, (s)->{
System.out.print(s.split(",")[0]);
}, new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(","+Integer.parseInt(s.split(",")[1]));
}
});
}
//消费方法
public static void consumer(String[] arr, Consumer<String> con1, Consumer<String> con2) {
for (String str : arr) {
con1.andThen(con2).accept(str);
}
}
}
b、Supplier
- 表示结果的提供者。
- 并不要求每次调用供应商时都返回一个新的或不同的结果。
- 这是一个函数式接口,其函数方法是get()。
public class SupplierTest {
public static void main(String[] args) {
String s1 = getString(() -> {
return "彭于晏";
});
System.out.println("lambda表达式:"+s);
}
//消费方法
public static String getString(Supplier<String> sup){
return sup.get();
}
}
c、Predicate
- 表示一个参数的谓词(布尔值函数)。
- 这是一个功能接口,其功能方法是test(Object)。
public class PredicateTest {
public static void main(String[] args) {
String[] arg = {"aaaa", "bb", "cccc"};
List<String> stringList = filter(arg
, (obj) -> obj.toString().startsWith("a")
, (obj) -> obj.toString().length() > 3);
System.out.println(stringList.get(0));
}
public static List<String> filter(String[] arg, Predicate p1, Predicate p2) {
List<String> result = new ArrayList<>();
for (String s : arg) {
if (p1.and(p2).test(s)) {
result.add(s);
}
}
return result;
}
}
d、Function
2、steam流的使用
需求:字符数组中去掉以首字为d开头,再得到所有长度大于3的字符串,再按字典序排序输出。
//自定义数组
String [] arg={"acc","ddd","aaa","bbb","ccc"};
Arrays.stream(arg)
.filter((obj)->obj.toString().startsWith("a"))
.filter((obj)->obj.toString().length()>3)
.forEach(System.out::println);
五、匿名内部类与lambda表达式区别
1、所需类型不同;
匿名内部类:接口,抽象类,也可具体类;
Lambda表达式: 接口;
2、使用限制
接口只有一个抽象方法,两者都可以使用;多个抽象方法,只能使用匿名内部类;
3、实现原理
匿名内部类,编译之后,产生单独的.class字节码文件;
lambda表达式:编译之后,没有单独的.class字节码文件,对应的字节码在运行时动态生成,不会保留到本地的硬盘之中;
觉得文章好不错的话三连支持一下吧!
别逼我求你……