JDK8新特性

2024-04-19

lambda表达式

格式

(形参列表) -> {方法体};
-> lambda操作符或箭头操作符
-> 左边是lambda形参列表,对应着要重写的接口的抽象方法的形参列表
-> 右边是lambda体,对应着接口的实现类要重写的方法的方法体
形参列表的数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
如果只有一个形参,参数的小括号可以省略
当lambda体只有一条语句时,return与大括号若有,都可以省略
只有抽象方法只有一个的接口可以用lambda表达式来体现

示例

// 无参无返回值
Runnable r1 = () -> {
	System.out.println("无参无返回值");
};
// 一个参数无返回值
Consumer<String> con1 = (String s) -> {
	System.out.println(s);
}
// 一个参数无返回值
Consumer<String> con1 = s -> {
	System.out.println(s);
}
(o1, o2) -> Integer.compare(o1, o2);

本质

一方面,做为接口的实现类的对象
另一方面,是一个匿名函数

函数式接口

如果接口中只声明有一个抽象方法,则此接口称为函数式接口
@FunctionalInterface注解表示是一个函数式接口
只有给函数式接口提供实现类的对象时,才能使用lambda表达式
jdk8中声明的函数式接口都在java.util.function包中

4个基本的函数式接口

// 消费型接口,对类型为T的对象应用操作方法,包含方法void accept(T t)
Consumer<T>
// 攻击型接口 返回类型为T的对象,包含方法T get()
Supplier<T>
// 函数型接口,对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法R apply(T t)
Function<T, R>
// 判断型接口,确定类型为T的对象是否满足某约束,并返回boolean值,包含方法boolean test(T t)
Predicate<T>

方法引用

可以看做是基于lambda表达式的进一步刻画
当满足一定条件的情况下,可以使用方法引入或构造器引用替换lambda表达式

本质

作为了函数式接口的实例

格式

类(对象):: 方法名

使用说明

对象::实例方法

// 函数式接口中的抽象方法a与内部实现时调用的对象的某个方法b的形参列表和返回值类型都相同,此时可以考虑使用方法b实现对方法a的替换、覆盖,此覆盖、替换即为方法引用
// 注意:此方法b是非静态方法,需要对象调用
Consumer<String> con2 = s -> System.out::println;
con2.accept("hello");

类::静态方法

// 函数式接口中的抽象方法a与内部实现时调用的对象的某个静态方法b的形参列表和返回值类型都相同,此时可以考虑使用方法b实现对方法a的替换、覆盖,此覆盖、替换即为方法引用
// 注意:此方法b是静态方法,需要类调用	
Comparator<Integer> com2 = Integer::compare;
com2.compare(1, 2);	

类::实例方法

// 函数式接口中的抽象方法a与内部实现时调用的对象的某个方法b的返回值类型相同,同时抽象方法a中有n个参数,方法b中有n-1个参数,且方法a的第一个参数做为方法b的调用者,方法a的后n-1个参数与方法b的n-1个参数的类型相同(或一致),此时可以考虑使用方法b实现对方法a的替换、覆盖,此覆盖、替换即为方法引用
// 注意:此方法b是非静态方法,需要对象调用,但是形式上,写出对象a所属的类
Comparator<String> com1 = new Comparator<String>() {
	@Override
	public int compare(Stirng o1, String o2) {
		return o1.compareTo(o2);
	}
};
// 方法引用
Comparator com2 = String::compareTo();
com2.compare("abc", "abd");

构造器引用

格式

类名::new

说明

调用了类名对应的类中的某个确定的构造器
具体调用的是类中的哪个构造器?取决于函数式接口的抽象方法的形参列表

示例

Supplier<A> a = new A {
	@Override
	public A apply() {
		return new A();
	}
}
// 构造器引用
Supplier<A> a1 = A::new;

Function<Integer, A> b = new Function(Integer, A) {
	@Override
	public A apply(Integer id) {
		return new A(id);
	}
};
// 构造器引用
Supplier<A> b1 = A::new; // 调用A类中参数是Integer/int类型的构造器

数组引用

格式

数组名[]::new

示例

Function<Integer, A[]> a = new Function<Integer, A[]>() {
	@Override
	public A apply(Integer length) {
		return new A[length];
	}
};
// 数组引用
Function<Integer, A> a1 = A[]::new;

Stream API

是真正的函数式编程风格引入java中
关注的是多个数据的计算(排序、查找、过滤、映射、遍历等),面向cpu的

说明

自己不会存储元素
不会改变源对象。会返回一个持有结果的新的Stream
操作是延迟执行的。意味者他们会等到需要结果的时候才执行。即一旦执行终止操作,就执行中间操作链,并产生结果
一旦执行了终止操作,就不能在调用其他中间操作或终止操作了

执行流程

实例化

// 通过集合
List<A> a = new ArrayList<>();
Stream<A> a1 = a.stream(a);
Stream<A> a2 = a.parallelStream(a);
// 通过数组
Stream<Integer> a = Arrays.stream(new Integer[]{1, 2, 3});
// 通过stream的of方法
Stream<Integer> a = Stream.of(1, 2, 3, 4);

一系列的中间操作

// 从流中排除某些元素
filter(Predicate)
// 通过流所生成的元素的hashCode()、equals()去除重复元素
distinct()
// 截断流,使其元素不超过指定的数量
limit(long maxSize)
// 跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流
skip(long n)
//	接收一个函数做为参数,将元素转换成其他形式或提取方法,该函数会被应用到每个元素上
map(Function f)
mapToDouble(ToDodbleFcuntion f)
mapToInt(ToIntFcuntion f)
mapToLong(ToLongFcuntion f)
flatMap(Fcuntion f)
// 自然排序
sorted()
// 定制排序
sorted(Comparator com)

执行终止操作

// 检查是否匹配所有元素
allMatch(Predicate p)
// 检查是否至少匹配一个元素
allMatch(Predicate p)
// 返回第一个元素,结果是一个Optional对象,使用.get()就能获取到数据
findFirst()
// 返回流中元素的总个数
count()
// 返回流中最大值,结果是一个Optional对象,使用.get()就能获取到数据
max(Comparator c)
// 返回流中最小值,结果是一个Optional对象,使用.get()就能获取到数据
min(Comparator c)
// 内部迭代
forEach(Consumer c)
// 可以将流中元素反复结合起来,得到一个值,返回T;identity是初始值
reduce(T identity, BinaryOperator b)
// 可以将流中元素反复结合起来,得到一个值,返回Optional<T>
reduce(BinaryOperator b)
// 将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
collect(Collector c)

Optional类

为了避免空指针异常
Optional<T>是一个容器类,可以保存类型T的值	

方法

// 实例化
Optional<T> Optional.ofNullable(T value)
// 当Optional实例内部的value属性不为null,则返回value,否则返回other
T orElse(T other)
// 获取内部的value值,value为null时则抛出异常
T get()


{/if}