Lambda 表达式
Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)
它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理:函数式开发者非常熟悉这些概念
用Lambda表达式实现Runnable
我们在这里做了什么呢?那就是用() -> {}代码块替代了整个匿名类。
// Java 8之前:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Before Java8, too much code for too little to do");
}
}).start();
//Java 8方式:
new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();
使用Java 8 lambda表达式进行事件处理
// Java 8之前:
JButton show = new JButton("Show");
show.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Event handling without lambda expression is boring");
}
});
// Java 8方式:
show.addActionListener((e) -> {
System.out.println("Light, Camera, Action !! Lambda expressions Rocks");
});
使用lambda表达式对列表进行迭代
// Java 8之前:
List<String> features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
for (String feature : features) {
System.out.println(feature);
}
// Java 8之后:
List features1 = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
features1.forEach(n -> System.out.println(n));
使用lambda表达式和函数式接口Predicate
除了在语言层面支持函数式编程风格,Java 8也添加了一个包,叫做 java.util.function。它包含了很多类,用来支持Java的函数式编程。其中一个便是Predicate,使用 java.util.function.Predicate 函数式接口以及lambda表达式,可以向API方法添加逻辑,用更少的代码支持更多的动态行为。下面是Java 8 Predicate 的例子,展示了过滤集合数据的多种常用方法。Predicate接口非常适用于做过滤。
public static void main(String[] args){
List<String> languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");
System.out.println("Languages which starts with J :");
filter(languages, (str)->((String) str).startsWith("J"));
System.out.println("Languages which ends with a ");
filter(languages, (str)->((String) str).endsWith("a"));
System.out.println("Print all languages :");
filter(languages, (str)->true);
System.out.println("Print no language : ");
filter(languages, (str)->false);
System.out.println("Print language whose length greater than 4:");
filter(languages, (str)->((String) str).length() > 4);
}
public static void filter(List<String> names, Predicate condition) {
for(String name: names) {
if(condition.test(name)) {
System.out.println(name + " ");
}
}
}
使用lambda表达式的Map和Reduce示例
// 为每个订单加上12%的税
// 老方法:
List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double total = 0;
for (Integer cost : costBeforeTax) {
double price = cost + 0.12*cost;
total = total + price;
}
System.out.println("Total : " + total);
// 新方法:
List<Integer> costBeforeTax1 = Arrays.asList(100, 200, 300, 400, 500);
double bill = costBeforeTax1.stream().map((cost) -> cost + 0.12*cost).reduce((sum, cost) -> sum + cost).get();
System.out.println("Total : " + bill);
接口的默认方法和静态方法
Java 8使用两个新概念扩展了接口的含义:默认方法和静态方法。
默认方法使得开发者可以在 不破坏二进制兼容性的前提下,往现存接口中添加新的方法,即不强制那些实现了该接口的类也同时实现这个新加的方法。
默认方法就是一个在接口里面有了一个实现的方法。
默认方法和抽象方法之间的区别在于抽象方法需要实现,而默认方法不需要。接口提供的默认方法会被接口的实现类继承或者覆写
private interface Defaulable {
// Interfaces now allow default methods, the implementer may or
// may not implement (override) them.
default String notRequired() {
return "Default implementation";
}
}
private static class DefaultableImpl implements Defaulable {
}
private static class OverridableImpl implements Defaulable {
@Override
public String notRequired() {
return "Overridden implementation";
}
}
Defaulable接口使用关键字default定义了一个默认方法notRequired()。DefaultableImpl类实现了这个接口,同时默认继承了这个接口中的默认方法;OverridableImpl类也实现了这个接口,但覆写了该接口的默认方法,并提供了一个不同的实现。
Java 8带来的另一个有趣的特性是在接口中可以定义静态方法,例子代码如下:
private interface DefaulableFactory {
// Interfaces now allow static methods
static Defaulable create( Supplier< Defaulable > supplier ) {
return supplier.get();
}
}
下面的代码片段整合了默认方法和静态方法的使用场景:
public static void main( String[] args ) {
Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );
System.out.println( defaulable.notRequired() );
defaulable = DefaulableFactory.create( OverridableImpl::new );
System.out.println( defaulable.notRequired() );
}
这段代码的输出结果如下:
Default implementation
Overridden implementation
由于JVM上的默认方法的实现在字节码层面提供了支持,因此效率非常高。默认方法允许在不打破现有继承体系的基础上改进接口。该特性在官方库中的应用是:给java.util.Collection接口添加新方法,如stream()、parallelStream()、forEach()和removeIf()等等。
尽管默认方法有这么多好处,但在实际开发中应该谨慎使用:在复杂的继承体系中,默认方法可能引起歧义和编译错误。如果你想了解更多细节,可以参考官方文档。
Date Time API
加强对日期与时间的处理。
jdk1.8新增了java.time包和java.time中的类
Java 8引入了新的Date-Time API(JSR 310)来改进时间、日期的处理。时间和日期的管理一直是最令Java开发者痛苦的问题。java.util.Date和后来的java.util.Calendar一直没有解决这个问题。
因为上面这些原因,诞生了第三方库Joda-Time,可以替代Java的时间管理API。Java 8中新的时间和日期管理API深受Joda-Time影响,并吸收了很多Joda-Time的精华。新的java.time包包含了所有关于日期、时间、时区、Instant(跟日期类似但是精确到纳秒)、duration(持续时间)和时钟操作的类。新设计的API认真考虑了这些类的不变性(从java.util.Calendar吸取的教训),如果某个实例需要修改,则返回一个新的对象。
我们接下来看看java.time包中的关键类和各自的使用例子。首先,Clock类使用时区来返回当前的纳秒时间和日期。Clock可以替代System.currentTimeMillis()和TimeZone.getDefault()。
// Get the system clock as UTC offset
final Clock clock = Clock.systemUTC();
System.out.println( clock.instant() );
System.out.println( clock.millis() );
上述例子的输出结果如下:
2018-04-23T06:59:48.587Z
1524466788738
第二,关注下LocalDate和LocalTime类。LocalDate仅仅包含ISO-8601日历系统中的日期部分;LocalTime则仅仅包含该日历系统中的时间部分。这两个类的对象都可以使用Clock对象构建得到。
// Get the system clock as UTC offset
final Clock clock = Clock.systemUTC();
// Get the local date and local time
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now(clock);
System.out.println(date);
System.out.println(dateFromClock);
// Get the local date and local time
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now(clock);
System.out.println(time);
System.out.println(timeFromClock);
上述例子的输出结果如下:
2018-04-23
2018-04-23
15:17:58.641
07:17:58.641
Stream API
新增的Stream API(java.util.stream)将生成环境的函数式编程引入了Java库中。这是目前为止最大的一次对Java库的完善,以便开发者能够写出更加有效、更加简洁和紧凑的代码。
- Steam API极大得简化了集合操作
生成流
在 Java 8 中, 集合接口有两个方法来生成流:
-
stream() − 为集合创建串行流。
-
parallelStream() − 为集合创建并行流。
package com.me.util;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* @Author yanyg
* @Date 2020/6/10 16:37
* @Descripetion admin
*/
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>();
Random r = new Random();
for (int i = 0; i < 10000; i++) {
nums.add(1000000 + r.nextInt(1000000));
}
// 一般遍历
long start = System.currentTimeMillis();
nums.forEach(v -> isPrime(v));
long end = System.currentTimeMillis();
System.out.println("一般遍历耗时:" + (end - start));
// stream 遍历
start = System.currentTimeMillis();
nums.stream().forEach(v -> isPrime(v));
end = System.currentTimeMillis();
System.out.println("stream 遍历耗时:" + (end - start));
// parallelStream 遍历
start = System.currentTimeMillis();
nums.parallelStream().forEach(Main::isPrime);
end = System.currentTimeMillis();
System.out.println("parallelStream 遍历耗时:" + (end - start));
}
static boolean isPrime(int num) {
for (int i = 2; i <= num / 2; i++) {
if (num % i == 0) return false;
}
return true;
}
}
Function API
-
Java8新引入函数式编程方式,大大的提高了编码效率
-
首先需要清楚一个概念:函数式接口;它指的是有且只有一个未实现的方法的接口,一般通过FunctionalInterface这个注解来表明某个接口是一个函数式接口。函数式接口是Java支持函数式编程的基础。
参考文档:
- https://blog.csdn.net/u014470581/article/details/54944384
- http://www.importnew.com/16436.html