文章目录
前言
提示:介绍Java8的新特性,包括Lambda表达式、函数式接口、方法引用、Optional、Stream流、日期类、其他
参考:https://www.runoob.com/java/java8-new-features.html
https://www.cnblogs.com/woyaobianfei/p/9187127.html
1、Lambda表达式
Lambda 表达式 − Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。
在JDK8之前,一个方法能接受的参数都是变量,例如: object.method(Object o)
那么,如果需要传入一个动作呢?比如回调。
那么你可能会想到匿名内部类。
例如:
匿名内部类是需要依赖接口的,所以需要先定义个接口
public interface PersonCallBack {
void callBack(Person person);
}
Person类
public class Person {
private int id;
private String name;
public Person (int id, String name) {
this.id = id;
this.name = name;
}
/**
* 创建一个Person后进行回调
* @param id
* @param name
* @param personCallBack
*/
public static void create(Integer id, String name, PersonCallBack personCallBack) {
Person person = new Person(id, name);
personCallBack.callBack(person);
}
}
// 调用方法,传入回调类,传统方式,使用匿名内部类
Person.create(1, "zhangsan", new PersonCallBack() {
@Override
public void callBack(Person person) {
System.out.println("callback -- " +person.getName());
}
});
上面的PersonCallback其实就是一种动作,但是我们真正关心的只有callback方法里的内容而已,我们用Lambda
表示,可以将上面的代码就可以优化成:
// 使用lambda表达式实现
Person.create(2, "lisi", (person) -> {System.out.println("lambda callback -- " +person.getName());});
// 进一步简化
// 这归功于Java8的类型推导机制。因为现在接口里只有一个方法,那么现在这个Lambda表达式肯定是对应实现了这个方法,
// 既然是唯一的对应关系,那么入参肯定是Person类,所以可以简写,
// 并且方法体只有唯一的一条语句,所以也可以简写,以达到表达式简洁的效果。
Person.create(2, "lisi", person -> System.out.println("lambda callback -- " +person.getName()));
Lambda允许把函数作为一个方法的参数,一个lambda由用逗号分隔的参数列表、–>符号、函数体三部分表示。
一个Lambda表达式实现了接口里的有且仅有的唯一一个抽象方法。那么对于这种接口就叫做函数式接口。
Lambda表达式其实完成了实现接口并且实现接口里的方法这一功能,也可以认为Lambda表达式代表一种动作,
我们可以直接把这种特殊的动作进行传递。
2、函数式接口
函数式接口是新增的一种接口定义。
用@FunctionalInterface修饰的接口叫做函数式接口,或者,函数式接口就是一个只具有一个抽象方法的普通接口,@FunctionalInterface可以起到校验的作用。
@FunctionalInterface
public interface MyFunctionInterface {
/**
* 函数式接口的唯一抽象方法
*/
void method();
}
在JDK7中其实就已经有一些函数式接口了,比如Runnable、Callable、FileFilter等等。
在JDK8中也增加了很多函数式接口,比如java.util.function包。
比如这四个常用的接口:
一个Lambda表达式其实也可以理解为一个函数式接口的实现者,但是作为表达式,它的写法
其实是多种多样的,比如
- () -> {return 0;},没有传入参数,有返回值
- (int i) -> {return 0;},传入一个参数,有返回值
- (int i) -> {System.out.println(i)},传入一个int类型的参数,但是没有返回值
- (int i, int j) -> {System.out.println(i)},传入两个int类型的参数,但是没有返回值
- (int i, int j) -> {return i+j;},传入两个int类型的参数,返回一个int值
- (int i, int j) -> {return i>j;},传入两个int类型的参数,返回一个boolean值
那么这每种表达式的写法其实都应该是某个函数式接口的实现类,需要特定函数式接口进行对应,比如上面的四种情况就分别对应:Supplier<T> , Function<T,R> , Consumer<T> , BiConsumer<T, U> , BiFunction<T, U, R> , BiPredicate<T, U> 。
Java8中提供给我们这么多函数式接口就是为了让我们写Lambda表达式更加方便,当然遇到特殊情况,你还是需要定义你自己的函数式接口然后才能写对应的Lambda表达式。
总的来说,如果没有函数式接口,就不能写Lambda表达式。
3、接口的默认方法和静态方法
在接口中用default修饰的方法称为默认方法。
接口中的默认方法一定要有默认实现(方法体),接口实现者可以继承它,也可以覆盖它。
/**
* 接口的默认方法
* @param s
* @return
*/
default String methodDefault(String s) {
System.out.println(s);
return "res--" + s;
}
在接口中用static修饰的方法称为静态方法。
/**
* 接口的静态方法
* @param a
* @param b
* @return
*/
static String methodStatic(String a, String b) {
return a + b;
}
4、方法引用
4.1、引用方法
- 实例对象::实例方法名
- 类名::静态方法名
- 类名::实例方法名
// 引用方法1 实例对象::实例方法名
// System.out代表的就是PrintStream类型的一个实例,println是这个实例的一个方法
// Consumer<String> consumer1 = s -> System.out.println(s);
Consumer<String> consumer2 = System.out::println;
consumer2.accept("呵呵");
// 引用方法2 类名::静态方法名
// Function中的唯一抽象方法apply方法参数列表与abs方法的参数列表相同,都是接收一个Long类型参数。
// Function<Long, Long> f = x -> Math.abs(x);
Function<Long, Long> f = Math::abs;
Long result = f.apply(-3L);
// 引用方法3 类名::实例方法名
// 若Lambda表达式的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,就可以使用这种方法
// BiPredicate<String, String> b = (x,y) -> x.equals(y);
BiPredicate<String, String> b = String::equals;
b.test("a", "b");
4.2、引用构造方法
// 引用构造方法
// 在引用构造方法的时候,构造方法参数列表要与接口中抽象方法的参数列表一致,格式为 类名::new
// Function<Integer, StringBuffer> fun = n -> new StringBuffer(n);
Function<Integer, StringBuffer> fun = StringBuffer::new;
StringBuffer buffer = fun.apply(10);
4.3、引用数组
// 引用数组
// 引用数组和引用构造器很像,格式为 类型[]::new,其中类型可以为基本类型也可以是类
// Function<Integer, int[]> fun = n -> new int[n];
Function<Integer, int[]> fun1 = int[]::new;
int[] arr = fun1.apply(10);
Function<Integer, Integer[]> fun2 = Integer[]::new;
Integer[] arr2 = fun2.apply(10);
5、Optional
Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
创建Optional对象的几个方法:
- Optional.of(T value), 返回一个Optional对象,value不能为空,否则会出空指针异常
- Optional.ofNullable(T value), 返回一个Optional对象,value可以为空
- Optional.empty(),代表空
其他API:
- optional.isPresent(),是否存在值(不为空)
- optional.ifPresent(Consumer<? super T> consumer), 如果存在值则执行consumer
- optional.get(),获取value
- optional.orElse(T other),如果没值则返回other
- optional.orElseGet(Supplier<? extends T> other),如果没值则执行other并返回
- optional.orElseThrow(Supplier<? extends X> exceptionSupplier),如果没值则执行exceptionSupplier,并抛出异常
高级API:
- optional.map(Function<? super T, ? extends U> mapper),映射,映射规则由function指定,返回映射值的Optional,所以可以继续使用Optional的API。
- optional.flatMap(Function<? super T, Optional< U > > mapper),同map类似,区别在于map中获取的返回值自动被Optional包装,flatMap中返回值保持不变,但入参必须是Optional类型。
- optional.filter(Pre