JDK8新特性之Lambda表达式
这里写目录标题
- 一、概念
- 1、基本概念
- 二、Lambda表达式的使用
- 1、函数式接口
- 2、语法
- 3、基本使用样例:
- 三、Lambda表达式在集合中使用
- 1、集合遍历
- 2、集合排序
- 3、集合过滤
- 4、集合映射
- 5、集合归约
- 四、方法引用
- 1、对象::实例方法
- 2、类::静态方法
- 3、类::实例方法
- 五、构造引用
- 六、总结
一、概念
1、基本概念
Lambda表达式是Java8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。举例说明下:
现在有一个接口:Message,里面包含抽象方法send(),表示message的发送动作。因为不同的消息发送方式不一样,所以具体方法在实现类中完成,这里就形成了如下代码:
Message:
public interface Message {void send();
}
实现方法调用:
public class MsgDemo {@Testpublic void test01(){sendMsg(new Message() {@Overridepublic void send() {System.out.println("这是一个Email信息");}});}public void sendMsg(Message msg){msg.send();}@Testpublic void test02(){sendMsg(()->{System.out.println("这是一个短信信息");});}
}
test01就是常规写法:sendMsg需要一个Message接口作为参数,其中Message接口的抽象方法send方法是用来实现具体方法的核心。为了省去Message的实现类,不得不使用匿名内部类,覆盖重写抽象方法send().
test02就是Lambda表达式去实现一个SMS消息的实现,简化了匿名内部类的使用,语法更加简单。
总而一句话:Lambda表达式是一种语法糖,它是用于简化匿名实现函数式接口的一种语法。
语法糖:是指在编程语言中引入的一种语法,它可以使代码更易读、更简洁,但并没有引入新的功能或改变语言的底层机制。语法糖并不会改变语言的语义,只是提供了一种更方便的编写方式。
二、Lambda表达式的使用
1、函数式接口
只有一个抽象方法的接口就是函数式接口,Lambda表达式的前置条件就是该接口必须是函数式接口。
@FunctionalInterface注解:该注解修饰的接口只能声明一个抽象方法。
其实我们的Lambda表达式就是对函数式接口的一种简写方式,所以只有是函数式接口,我们才能用Lambda表达式;再换句话说,Lambda表达式需要函数式接口的支持
2、语法
Lambda省去了面向对象的条条框框,它的标准格式由三个部分组成:
(参数类型 参数名称) -> {代码提;
}
参数列表:
表达式的参数列表位于圆括号中,因为入参的类型在接口中是定义好的,可以不写,只写参数名,参数名可以随意起名。参数可以是零个或多个,如果只有一个参数,圆括号可以省略。如果没有参数,则圆括号不可省略,使用空括号。
箭头符号:
箭头符号 -> 将参数列表与 Lambda 表达式的主体(或函数体)分隔开。
表达式主体:
表达式主体可以是一个表达式,也可以是一个代码块。方法体只有一句话的时候,{} 和 return 可以省略。
3、基本使用样例:
无返回值无形参的抽象方法:
public interface MyInterface1 {public abstract void show();}
public static void main(String[] args) {MyInterface1 test=new MyInterface1() {@Overridepublic void show() {System.out.println("show 方法执行了..");}};test.show();//简写MyInterface1 test1 =() ->System.out.println("show1 方法执行了..");test1.show();}
无返回值有形参的抽象方法:
public interface MyInterface2 {public abstract void show(int a,int b);}
public static void main(String[] args) {MyInterface2 test=new MyInterface2() {@Overridepublic void show(int a, int b) {System.out.println(a+b);}};test.show(10,20);//简写1:方法命可以自己推断出来MyInterface2 test1 = (int a,int b) ->{System.out.println(a+b);};test1.show(20,30);//简写2:可以省略形参类型MyInterface2 test2 = (a,b) ->{System.out.println(a+b);};test2.show(30,40);//简写3:如果抽象方法中只有一行代码,可以省略方法体的大括号,当然,如果不止一行,就不能省略MyInterface2 test3 = (a,b) ->System.out.println(a+b);test3.show(40,50);}
有返回值的抽象方法:
public interface MyInterface3 {public abstract int show(int a,int b);}
public static void main(String[] args) {MyInterface3 test=new MyInterface3() {@Overridepublic int show(int a, int b) {return a-b;}};System.out.println(test.show(20,10));//简写1:MyInterface3 test2 = (int a, int b) -> {return a - b;};System.out.println(test2.show(30, 10));//简写2:MyInterface3 test3=(a,b)->{return a-b;};System.out.println(test3.show(40,10));//简写3:有返回值的方法,不能直接去点大括号,还要去掉returnMyInterface3 test4=(a,b)->a-b;System.out.println(test4.show(50,10));}
三、Lambda表达式在集合中使用
为了能够让Lambda和Java的集合类集更好的一起使用,集合当中,也新增了部分接口,以便与Lambda表达式对接。要用Lambda遍历集合就一定要看懂源码。
1、集合遍历
private static List<String> list;static {list= new ArrayList<>();list.add("Java");list.add("Python");list.add("C++");}@Testpublic void test1(){list.forEach(s -> System.out.println(s));}
2、集合排序
@Testpublic void test2(){Collections.sort(list, (s1, s2) -> s2.compareTo(s1));}
3、集合过滤
@Testpublic void test3(){// 使用 Lambda 表达式过滤集合List<String> filteredList = list.stream().filter(s -> s.startsWith("P")).collect(Collectors.toList());System.out.println(filteredList); // 输出:[Python]}
4、集合映射
@Testpublic void test4(){// 使用 Lambda 表达式映射集合List<String> mappedList = list.stream().map(s -> s.toUpperCase()).collect(Collectors.toList());System.out.println(mappedList); // 输出:[JAVA, PYTHON, C++]}
5、集合归约
@Testpublic void test5(){List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);// 使用 Lambda 表达式归约集合int sum = list.stream().reduce(0, (a, b) -> a + b);System.out.println(sum); // 输出:6}
四、方法引用
方法引用其实是Lambda表达式的另一种写法,当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用;
注意: 实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!
方法引用:使用操作符::将方法名和对象或类的名字分隔开来,三种主要使用情况为:
- 对象::实例方法
- 类::静态方法
- 类::实例方法
1、对象::实例方法
public static void main(String[] args) {Consumer<String> consumer=new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}};consumer.accept("aaaaaaaa");//简写1:Consumer<String> consumer1 = (String s) -> {System.out.println(s);};consumer1.accept("abc");//abc//简写2:Consumer<String> consumer2 = (s) -> System.out.println(s);consumer2.accept("bcd");//简写3:Consumer<String> consumer3=System.out::println;consumer3.accept("efg");}
为什么可以写成上述方式?
因为:System.out.println(s);与void accept(String s)一样,都是使用s作为参数,返回值是void,因此就可以简写为简写3。
2、类::静态方法
public static void main(String[] args) {BinaryOperator<Double> operator=new BinaryOperator<Double>() {@Overridepublic Double apply(Double a, Double b) {return Math.max(a,b);}};System.out.println(operator.apply(1.0, 2.0));//2.0//简写1:BinaryOperator<Double> operator1=(o,o1)->Math.max(o,o1);System.out.println(operator1.apply(2.0, 3.0));//3.0//简写2:BinaryOperator<Double> operator2= Math::max;System.out.println(operator2.apply(3.0, 4.0));//3.0}
因为Math.max()所需要的参数以及返回值与重写的accet()一样,可以简写为类::静态方法;
public static void main(String[] args) {Comparator<Integer> comparator = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return Integer.compare(o1,o2);}};System.out.println(comparator.compare(20, 12));//1Comparator<Integer> comparator1=(a,b)->Integer.compare(a,b);System.out.println(comparator1.compare(10,20));//-1Comparator<Integer> comparator2=Integer::compareTo;System.out.println(comparator2.compare(10,10));//0}
3、类::实例方法
public static void main(String[] args) {Comparator<Integer> comparator = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return Integer.compare(o1,o2);}};System.out.println(comparator.compare(20, 12));//1Comparator<Integer> comparator1=(a,b)->Integer.compare(a,b);System.out.println(comparator1.compare(10,20));//-1Comparator<Integer> comparator2=Integer::compareTo;System.out.println(comparator2.compare(10,10));//0}
传递过来的两个参数,一个作为调用者,一个作为参数,这时候,使用类::实例方法简写;
五、构造引用
格式:ClassName::new
与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致!
public static void main(String[] args) {// 使用 Supplier 提供一个 Person 实例Supplier<Person> personSupplier = Person::new;// 延迟创建 Person 对象Person person = personSupplier.get();System.out.println("Person's name: " + person.getName()); // 输出 "John Doe"}
六、总结
Lambda表达式的优点很明显,在代码层次上来说,使代码变得非常的简洁。缺点也很明显,代码不易读
优点:
- 代码简洁,开发迅速
- 方便函数式编程
- 非常容易进行并行计算
- Java 引入 Lambda,改善了集合操作
缺点: - 代码可读性变差
- 在非并行计算中,很多计算未必有传统的 for 性能要高
- 不容易进行调试