Java8新特性, Lambda表达式与函数式接口
Java8的新特性有哪些
新特性的特征:
速度更快
代码更少(增加了新的语法:Lambda 表达式)
强大的 Stream API
便于并行
最大化减少空指针异常:Optional
Nashorn引擎,允许在JVM上运行JS应用
lambda表达式
什么是lambda表达式?
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以
传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更 灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了
提升
为什么要有lambda表达式?
Lambda表达式。它可以让你很简洁地表示一个行为或传递代码。现在你可以把Lambda 表达式看作匿名功能,它基本上就是没有声明名称的方法,但和匿名类一样,它也可以作为参 数传递给一个方法。
哪些场景可以使用到lambda表达式?
?????
首先我们看下再没有使用lambda的时候使用匿名内部类的对比
lambda与匿名内部类对比
//匿名内部类 Comparator<Integer> c1 = new Comparator<Integer>() { @Override public int compare(Integer t1, Integer t2) { return Integer.compare(t1,t2); } }; int compareNumOne = c1.compare(12,13); System.out.println(compareNumOne); //-1 System.out.println("==========Lambda=========="); // lambda表达式 Comparator<Integer> c2 = (c3,c4) -> Integer.compare(c3,c4); int compareNumTwo = c2.compare(11,10); System.out.println(compareNumTwo); // 1
可以看出lambda表达式简洁了很多,直接了,不像以前这么难懂 还有一大堆的内部逻辑
匿名内部类
Button button = new Button("Send");
button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
label.setText("Sent!!");
}
});
这里,setOnAction方法的行为就用EventHandler参数化了。用Lambda表达式的话,看 起来就是这样:
button.setOnAction((ActionEvent event) -> label.setText("Sent!!"));
上面可以看出lambda使用起来很方便的 简单明了
lambda的使用
lambda的结构
参数列表——这里它采用了Comparator中compare方法的参数,两个Apple。 箭头——箭头->把参数列表与Lambda主体分隔开。 Lambda主体——比较两个Apple的重量。表达式就是Lambda的返回值了。
1.举例: (o1,o2) -> Integer.compare(o1,o2); * 2.格式: * -> :lambda操作符 或 箭头操作符 * ->左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表) * ->右边:lambda体 (其实就是重写的抽象方法的方法体)
Lambda 表达式:在Java 8 语言中引入的一种新的语法元素和操 作符。这个操作符为 “->” ,该操作符被称为 Lambda 操作符 或箭头操作符。
它将 Lambda 分为两个部分:
左侧:指定了 Lambda 表达式需要的参数列表
右侧:指定了 Lambda 体,是抽象方法的实现逻辑,也即 Lambda 表达式要执行的功能。
下面给出了Java 8中五个有效的Lambda表达式的例子
Lambda表达式的使用(分六种)
语法格式一:无参,无返回值 |
Runnable r1 = () -> {System.out.println("Hello Lambda");}; r1.run(); |
语法格式二:Lambda需要一个参数,但是没有返回值 |
Consumer<String> con = (String s) -> {System.out.println(s);}; con.accept("value"); |
语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断” |
Consumer<String> con = (s) -> {System.out.println(s);}; con.accept("value"); |
语法格式四:Lambda若只需要一个参数,参数的小括号可以省略 |
Consumer<String> con = s -> {System.out.println(s);}; con.accept("value"); |
语法格式五:Lambda需要两个以上的参数,多条执行语句,并且可以有返回值 |
Consumer<Integer> con = (x,y) -> { System.out.println(x); return x.compareTo(y); } |
语法格式六:当Lambda体只有一条语句时,return与大括号 |
Comparator<Integer> com = (o1, o2) -> o1.compareTo(o2); com.compreTo(10,13); |
// 语法格式一:无参,无返回值 @Test public void TestPOne(){ Runnable r1 = new Runnable() { @Override public void run() { System.out.println(" I'm one"); } }; r1.run(); System.out.println("=======lambda======"); Runnable r2 = () -> System.out.println("I'm lambda'One"); r2.run(); } // 语法格式二:Lambda 需要一个参数,但是没有返回值。 @Test public void TestTwo(){ Consumer<String> con1 = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }; con1.accept("老王"); System.out.println("=======lambda======"); Consumer<String> con2 = (String s1) -> {System.out.println(s1);}; con2.accept("我是lambda老王"); } //语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断” // 类型推断想我们之前遇到的泛型和数组一样 /* Consumer<String> con = new Consumer<>(); // 省略后面的String类型 int [] len = new int [] {1,2,3}; 可以 写成 int [] len = {1,2,3} */ @Test public void TestThree(){ Consumer<String> con = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }; con.accept("老王头"); System.out.println("=======lambda======"); Consumer<String> con1 = (s) -> { System.out.println(s); }; con1.accept("吾乃lambda老王头"); } //语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略 @Test public void TestFour(){ Consumer<String> con = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }; con.accept("老刘"); System.out.println("=======lambda======"); Consumer<String> con1 = s -> {System.out.println(s);}; con1.accept("吾乃lambda老刘"); } //语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值 @Test public void TestFive(){ Comparator<Integer> com = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); } }; System.out.println(com.compare(13, 14)); System.out.println("=======lambda======"); Comparator<Integer> com1 = (l1,l2) -> { System.out.println(l1); System.out.println(l2); return l1.compareTo(l2); }; System.out.println(com1.compare(11, 10)); } //语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略 @Test public void TestSix(){ // 原有 Comparator<Integer> com = (o1, o2) -> {return o1.compareTo(o2);}; System.out.println(com.compare(10, 11)); //优化后 Comparator<Integer> com1 = (l1, l2) -> l1.compareTo(l2); System.out.println(com1.compare(20, 10)); }
由上可以看出lambda只是修改了 以前写法的方法体 方法后面的执行和参数的传递依旧时不变的 只需要看方法体怎么改变即可。
Summary
* 3. Lambda表达式的使用:(分为6种情况介绍) * * 总结: * ->左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只有一个参数,其一对()也可以省略 * ->右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是return语句),省略这一对{}和return关键字 * * 4.Lambda表达式的本质:作为函数式接口的实例 * * 5. 如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。我们可以在一个接口上使用 @FunctionalInterface 注解, * 这样做可以检查它是否是一个函数式接口。 * * 6. 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
函数式接口
什么是函数式接口
只包含一个抽象方法的接口,称为函数式接口。
为什么要有函数式接口
Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP) 编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,
Java不 得不做出调整以便支持更加广泛的技术要求,也即java不但可以支持OOP还 可以支持OOF(面向函数编程)
说白了 函数式接口就是为了让java能面向函数编程而创建的
函数式接口的作用
用函数式接口可以干什么呢?Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例(具体说来,是函数式接口一个具体实现 的实例)
你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式 抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽 象方法上进行声明)。
我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检 查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个 接口是一个函数式接口。
在java.util.function包下定义了Java 8 的丰富的函数式接口
简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是 Lambda表达式和函数式接口的关系。也就是说,
只要一个对象是函数式接口 的实例,那么该对象就可以用Lambda表达式来表示。
所以以前用匿名实现类表 示的现在都可以用Lambda表达式来写。
自定义函数式接口
只需要在自定义的接口上加入 @FunctionalInterface 注解即可让这个接口成为函数事接口
@FunctionalInterface public interface MyInterface { public void method1(); }
自定义函数接口与Lambda的使用
Java 内置四大核心函数式接口
其它接口
Lambda与函数式接口的使用
// 根据传递参数输出信息 public void happyTime(double money, Consumer<Double> con){ con.accept(money); } //根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定 public List<String> filterString(List<String> list, Predicate<String> pre){ List<String> filterList = new ArrayList<>(); for (String s:list ) { if(pre.test(s)){ // 如果参数s满足定义的Predicate的String泛型 filterList.add(s); } } return filterList; } @Test public void TestMonday(){ happyTime(500, new Consumer<Double>() { @Override public void accept(Double aDouble) { System.out.println("去隔壁洗浴中心消费"+aDouble); } }); System.out.println("=====lambda===="); happyTime(4000, money -> System.out.println("买了大宝剑花费"+money)); } @Test public void TestTuesday(){ List<String> list = Arrays.asList("老王", "老张头","隔壁老王","隔壁小姐姐"); List<String> list1 = filterString(list, new Predicate<String>() { @Override public boolean test(String s) { return s.contains("老"); // 包含老的字符串 } }); System.out.println(list1); List<String> list2 = filterString(list,s -> s.contains("老") ); System.out.println(list2); }
lambda与方法搭配使用必须满足函数式接口的要求,
下面既不是接口种的方法
Practices
1:
(1) 这个Lambda没有参数,并返回void。它类似于主体为空的方法:public void run() {}。 (2) 这个Lambda没有参数,并返回String作为表达式。 (3) 这个Lambda没有参数,并返回String(利用显式返回语句)。 (4) return是一个控制流语句。要使此Lambda有效,需要使花括号,如下所示: (Integer i) -> {return "Alan" + i;}。 (5)“Iron Man”是一个表达式,不是一个语句。要使此Lambda有效,你可以去除花括号 和分号,如下所示:(String s) -> "Iron Man"。或者如果你喜欢,可以使用显式返回语 句,如下所示:(String s)->{return "IronMan";}。
2: 下面哪些接口是函数式接口?
public interface Adder{ int add(int a, int b); } public interface SmartAdder extends Adder{ int add(double a, double b); } public interface Nothing{ }
答案:只有Adder是函数式接口。 SmartAdder不是函数式接口,因为它定义了两个叫作add的抽象方法(其中一个是从 Adder那里继承来的)。 Nothing也不是函数式接口,因为它没有声明抽象方法。
.