一、函数式接口
函数式接口的定义:
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
函数式接口可以被隐式转换为 lambda 表达式。
//如下就是一个函数式的接口
interface MathOperation {
int operation(int a, int b);
}
函数式接口本身并没有过多的价值,主要是配合Lambda表达式进行使用。如果非函数式接口那么就不能使用Lambda表达式,还是得回归之前的匿名函数。
@FunctionalInterface的妙用,如果不确定自己的接口是不是函数式接口,可以在接口上加上,如果不是编辑会报错的。
二、Lambda表达式
Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
这种写法有一定的局限性,就是必须是函数式接口,当然好处就是语法非常的简洁明了。
//如下就是一个函数式的接口
interface MathOperation {
int operation(int a, int b);
}
public static int operate(int a, int b, MathOperation mathOperation) {
return mathOperation.operation(a, b);
}
public static void main(String[] args) {
/**
* java 8之前的使用姿势,结果为15
*/
System.out.println(operate(5, 10, new MathOperation() {
@Override
public int operation(int a, int b) {
return a + b;
}
}));
/**
* java 8的使用姿势,结果都为15
* 1:Lambda表达式会自动推断类型,所以类型声明可以省略
* 2:当Lambda表达式右侧只有一个简单语句,例如a+b,那么可以自动推断出返回值,不用加return
* 3:如果处理逻辑比较复杂,那么需要加上{}和return
*/
System.out.println(operate(5, 10, (int a, int b) -> a + b));
System.out.println(operate(5, 10, (int a, int b) -> { return a + b; }));
System.out.println(operate(5, 10, (a, b) -> a + b));
}
lambda 表达式内部的变量作用域其实跟匿名函数也是一样的,如果lambda表达式访问局部变量或者成员变量都是需要使用final来修饰,当然lambda的语法并没有那么严格,可以不使用final进行修饰,但是要保证lambda表达式不能对变量进行修改。
int variable = 22;
final int variable1 = 22;
//这样都是合法的lambda表达式
System.out.println(operate(5,10,(a,b) -> a+b+variable));
System.out.println(operate(5,10,(a,b) -> a+b+variable1));
//这样子就会报错
System.out.println(operate(5,10,(a,b) -> {
variable = 222;
}));
至于为什么lambda表达式或者说匿名函数会这样要求,其实原因也很简单,究其根本就是生命周期和值拷贝。
成员变量(非static)一旦类被实例化就会存在堆内存当中,生命周期跟随实例创建而创建,跟随实例销毁而销毁。
局部变量,存在栈当中,方法调用而产生,调用完成就释放。
lambda表达式/匿名函数其实是跟外部类同级的,如果使用了局部变量,局部变量在方法调用完成就释放了,但是lambda表达式还存在,这就比较尴尬了,所以java的做法是将局部变量的值copy一份使用,如果是引用型变量,copy的就是地址,既然是值copy,为了能够局部变量使用效果一样,自然是不允许修改值的,不然调用就会产生不同的效果。
三、方法引用
方法引用也是java 8推出的一个特性,可以使用::来指向对应方法,在介绍方法引用之前先介绍一下java 8推出的新的函数式接口。
//Consumer<T>其实可以理解为就是一个消费者,需要传递一个Class<T>给Consumer,然后可以自己实现accept的操作
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
//Supplier<T>其实可以理解成一个容器,用于存放Class<T>,然后可以自定义get的操作
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
//在java 8之前可以使用匿名函数这么玩
Consumer<Integer> consumer = new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
};
consumer.accept(10);
Supplier<Integer> supplier = new Supplier<Integer>() {
@Override
public Integer get() {
return Integer.MAX_VALUE;
}
};
System.out.println(supplier.get());
理解了上述两个函数式接口的主要用处,接下来可以结合着方法引用来使用。先定义一个测试类,里面有静态方法和非静态方法。
package com.cainiao.wmp.service;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* @author huayi.zh
* @date 2020/09/14
*/
public class Java8Test {
static class Car {
//Supplier是jdk1.8的接口,这里和lambda一起使用了
public static Car create(final Supplier<Car> supplier) {
return supplier.get();
}
public static void collide(final Car car) {
System.out.println("Collided " + car.toString());
}
public void follow(final Car another) {
System.out.println("Following the " + another.toString());
}
public void repair() {
System.out.println("Repaired " + this.toString());
}
}
public static void main(String[] args) {
/**
* 使用方法1:引用创建新实例,可以发现Supplier<Car>起到了装配Car实例的作用
*/
Supplier<Car> supplier = Car::new;
Car car = Car.create(supplier);
/**
* 使用方法2:借助forEach方法,用::调用静态函数,其实Car::collide产生一个Consumer<Car>,需要接收一个Car实例,
* 自然就可以使用forEach方法了,可以将Car::collide看做是对Consumer<Car>接口的一个实现,具体内容就是接收一个
* Car实例,然后执行collide方法。
*/
List<Car> cars = Arrays.asList(car);
// Consumer<Car> collide = Car::collide;
// cars.forEach(collide);
cars.forEach(Car::collide);
/**
* 使用方法3:借助forEach方法,用::调用普通函数,分为两种情况,一种是Car类,一种是Car的实例
* cars.forEach(Car::repair);可以执行,但是cars.forEach(Car::follow);却不行
*/
Consumer<Car> repair = Car::repair;
BiConsumer<Car, Car> follow = Car::follow;
/**
* 原因很明显了,follow方法参数需要提供一个Car实例,本身又是非静态函数,需要用实例才能调用,需要两个Car实例,
* 但是repair是空参函数,所以只需要一个,forEach每次循环只能提供一个Car实例
*/
/**
* 换一种写法,使用Car的实例,然后加上::也是调用的
*/
cars.forEach(car::follow);
}
}
四、默认方法
Java 8 新增了接口的默认方法。
简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。
我们只需在方法名前面加个 default 关键字即可实现默认方法。
为什么要有这个特性?
首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的 java 8 之前的集合框架没有 foreach 方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。
//默认方法语法格式如下:
public interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
}
java都是单继承,多实现的,目的就是为了避免多个继承,存在冲突实现的方法,那么现在在接口上增加了默认方法,也会存在同样的问题,java 8是这样处理的。
//第一个解决方案是创建自己的默认方法,来覆盖重写接口的默认方法:
public class Car implements Vehicle, FourWheeler {
default void print(){
System.out.println("我是一辆四轮汽车!");
}
}
//第二种解决方案可以使用 super 来调用指定接口的默认方法:
public class Car implements Vehicle, FourWheeler {
public void print(){
Vehicle.super.print();
}
}
五、Stream
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
在 Java 8 中, 集合接口有两个方法来生成流:
- stream() − 为集合创建串行流。
- parallelStream() − 为集合创建并行流。
常见算子有:
forEach:可以迭代流中的每个数据
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
//forEach可以迭代流中每个元素,具体做什么操作,可以使用lambda表达式来实现。
strings.stream().forEach(s-> System.out.println(s));
//等同于以下实现,只不过使用lambda表达式会使代码更加的简洁紧凑。
strings.stream().forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//forEach的源码,可以发现参数一个Consumer<? super T> action,跟之前的方法引用可以对上,那是不是也可以用方法引用来实现?
//答案当然是可以的,说明lambda表达式其实就是一种特殊的方法引用,
void forEach(Consumer<? super T> action);
//如下来实现
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
strings.stream().forEach(System.out::println);
map:****用于映射每个元素到对应的结果
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
strings.stream().map(s -> "\"" + s + "\"").forEach(s -> System.out.println(s));
filter:根据规则过滤流中每个元素
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
strings.stream().filter(s->s != "").forEach(s-> System.out.println(s));
limit:控制获取指定数量的流
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
strings.stream().limit(1).forEach(s-> System.out.println(s));
sorted:对流中每个元素进行排序,自定义类需要自己实现compare方法
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
strings.stream().sorted((a,b)->a.compareTo(b)).forEach(s-> System.out.println(s));
Collectors:Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
String collect = strings.stream().collect(Collectors.joining(","));
List<String> collect1 = strings.stream().collect(Collectors.toList());
六、Optional类
Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional 类的引入很好的解决空指针异常。
常用方法:
1:static <T> Optional<T> empty()
返回空的 Optional 实例。
Optional<Integer> v11 = Optional.empty();
2:****static <T> Optional<T> ofNullable(T value)
如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。
Integer v1 = new Integer(10);
Integer v2 = null;
//ofNullable可以传入null,返回一个空的Optional<Integer>,使用get()会抛出异常
Optional<Integer> v11 = Optional.ofNullable(v1);
System.out.println(v11.get());
3:****static <T> Optional<T> of(T value)
返回一个指定非****null****值的****Optional****。
Integer v1 = new Integer(10);
Integer v2 = null;
//of不允许传入null,会抛出异常
Optional<Integer> v11 = Optional.of(v2);
4:****boolean equals(Object obj)
判断其他对象是否等于 Optional****。
Integer v1 = new Integer(10);
Integer v2 = null;
Optional<Integer> v11 = Optional.ofNullable(v1);
Optional<Integer> v21 = Optional.ofNullable(v1);
boolean equals = v11.equals(v21);
//true
System.out.println(equals);
5:****T get()
如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException
Integer v1 = new Integer(10);
Integer v2 = null;
Optional<Integer> v11 = Optional.ofNullable(v1);
System.out.println(v11.get());//10
6:****boolean isPresent()
如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException
Integer v1 = new Integer(10);
Integer v2 = null;
Optional<Integer> v11 = Optional.ofNullable(v1);
System.out.println(v11.isPresent());//true
Optional<Integer> v21 = Optional.ofNullable(v2);
System.out.println(v21.isPresent());//false
7:T orElse(T other)
如果存在该值,返回值, 否则返回 other****。
Integer v1 = new Integer(10);
Integer v2 = null;
Optional<Integer> v11 = Optional.ofNullable(v1);
System.out.println(v11.orElse(20));//10
Optional<Integer> v21 = Optional.ofNullable(v2);
System.out.println(v21.orElse(20));//20