Java8新特性
函数式接口
概念
- 有且只有一个抽象方法的接口,称之为函数式接口
- 接口中可以包含其他的方法(默认,静态,私有),但是只能有一个抽象方法。
@FunctionalInterface
注解- 检测接口是否是一个函数式接口
- 是;编译成功
- 否;编译失败(接口中没有抽象方法或抽象方法的个数大于1)
- 格式;
1
2
3
4
public interface 接口名{
抽象方法
}
- 检测接口是否是一个函数式接口
使用;一般可以作为方法的参数和返回值类型
函数式编程
Lambda的延迟执行
- Lambda特点;延迟执行
- 作用;提升性能
常用函数式接口
Supplier接口
java.util.function.Supplier<T>
;接口仅包含一个无参的方法;T get()
。用来获取一个泛型参数指定类型的对象数据。- 生产型接口;指定接口的泛型是什么类型,那么接口中的get方法就会产生什么类型的数据
Consumer接口
java.util.function.Consumer<T>
;接口包含抽象方法void accept(T t)
,意为消费一个指定泛型数据- 消费型接口,泛型执行什么类型,就可以使用accept方法消费什么类型的数据,至于具体怎么消费,需要定义计算
- 默认方法;
addThen
- 需要两个Consumer接口,可以把两个Consumer接口组合到一起,在对数据进行消费
- 源码;
1
2
3
4default Consumer<T andThen(Consumer<? super T> after){
Objects.requirNonNull(after);
return (T t)->{accept(t);after.accept(t);};
}
Predicate接口
java.util.function.Predicate<T>
接口;对某种数据类型的数据进行判断,结果返回一个Boolean值- 抽象方法;
boolean test(T t)
;用来对指定数据类型数据进行判断的方法 默认方法;
add
;与1
2
3
4default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) ‐> test(t) && other.test(t);
}or
;或1
2
3
4default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) ‐> test(t) || other.test(t);
}negate
;非1
2
3default Predicate<T> negate() {
return (t) ‐> !test(t);
}
Function接口
java.util.function.Function<T,R>
;接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件- 抽象方法;
R apply(T t)
;根据类型T的参数获取类型R的的结果(一般用于类型转换) - 默认方法;
andThen
Stream流式思想
- JDK1.8之后
- 关注做什么,不关注怎么做
- “Stream流”其实是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不存储任何元素(或其地址值)
- Stream(流)是一个来自数据源的元素队列
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源流的来源。 可以是集合,数组等。
- Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者增强for的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式,流可以直接调用遍历方法。
- 使用步骤
- 获取数据源
- 数据转换
- 执行操作获取想要的结果
- 注意;
- 两种方法
default Stream<E> stream()
根据Stream接口获取流
static <T> Stream<T> of(T...values)
;参数是一个可变参数,可以传递一个数组
常用方法
- 延迟方法;返回值类型仍然是
Stream
接口自身类型的方法,因此支持链式调用。 - 终结方法;返回值类型不再是
Stream
接口自身类型的方法,因此不再支持类似StringBuilder
那样的链式调用逐一处理;foreach
void forEach(Consumer<? super T> action)
;该方法接收一个Consumer
接口函数,会将每一个流元素交给该函数进行处理- forEach方法,用来遍历流中的数据,是一个终结方法,遍历之后就不能继续调用Stream流中的其他方法
过滤;filter
Stream<T> filter(Predicate<? super T> predicate)
;可以通过filter方法将一个流转换为另一个子集流映射;map
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
;将流中的元素映射到另一个流中,可以使用map方法统计个数;count
long count()
;用于统计Stream流中元素的个数- count方法是一个终结方法,返回值是一个long类型的整数,所有不能再继续调用Stream流中的其他方法
取用前几个;limit
Stream<T> limit(long maxSize);
;用于截取流中的元素- limit是一个延迟方法,只是对流中的元素进行截取,返回是一个新的流,所以可以继续调用Stream流中的其他方法
跳过前几个;skip
Stream<T> skip(long n);
;用于跳过元素- 如果流的当前长度大于n,则跳过前n个,否则会得到一个长度为0的空流
组合;concat
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
;将两个流合并为一个流- 这是一个静态方法,与String中的concat方法不同
方法引用
- 方法引用符:双冒号
::
为引用运算符,而它所在的表达式被称为方法引用
。如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者 - 语义;
- Lambda表达式写法:
s -> System.out.println(s)
;拿到参数之后经Lambda之手,继而传递给 System.out.println 方法去处理。 - 方法引用写法:
System.out::println
;直接让System.out
中的println
方法来取代Lambda
。两种写法的执行效果完全一样,而第二种方法引用的写法复用了已有方案,更加简洁。注:Lambda中传递的参数一定是方法引用中的那个方法可以接收的类型,否则会抛出异常
- Lambda表达式写法:
- 通过对象名引用成员方法
- 通过对象名引用成员方法,使用前提是对象名已经存在,成员方法也是已经存在的
- 通过类名称引用静态方法
- 通过super引用成员方法
- 通过this引用成员方法、
- 类的构造器引用
- 数组的构造器引用