Java8新特性
方法引用
前言
什么是函数式接口
只包含一个抽象方法的接口,称为函数式接口。
可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。
可以在一个接口上使用
@FunctionalInterface
注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
如何理解函数式接口
- 在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。
- 简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
- 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
方法引用
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
要求:实现接口的抽象方法的参数列表和返回值类型,必须与引用的方法的参数列表和返回值类型保持一致!
格式:使用操作符 ::
将类(或对象) 与 方法名分隔开来。
如下三种主要使用情况:
对象实例 :: 方法名
类 :: 静态方法名
类 :: 实例方法名
例如x -> System.out.println(x)
等价于 System.out::println
举例
List<Integer> list = Arrays.asList(82,22,34,50,9);
//原始写法
list.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
System.out.println(list);
//lambda方式
list.sort((o1,o2) -> o2 - o1);
System.out.println(list);
//方法引用
list.sort(Integer::compareTo);
System.out.println(list);
/*
输出结果
[82, 50, 34, 22, 9]
[82, 50, 34, 22, 9]
[9, 22, 34, 50, 82]
*/
// 情况一:对象 :: 实例方法
//Consumer中的void accept(T t)
//PrintStream中的void println(T t)
@Test
public void test1() {
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("北京");
System.out.println("*******************");
PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
con2.accept("beijing");
}
//Supplier中的T get()
//Employee中的String getName()
@Test
public void test2() {
Employee emp = new Employee(1001,"Tom",23,5600);
Supplier<String> sup1 = () -> emp.getName();
System.out.println(sup1.get());
System.out.println("*******************");
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
}
// 情况二:类 :: 静态方法
//Comparator中的int compare(T t1,T t2)
//Integer中的int compare(T t1,T t2)
@Test
public void test3() {
Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
System.out.println(com1.compare(12,21));
System.out.println("*******************");
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(12,3));
}
//Function中的R apply(T t)
//Math中的Long round(Double d)
@Test
public void test4() {
Function<Double,Long> func = new Function<Double, Long>() {
@Override
public Long apply(Double d) {
return Math.round(d);
}
};
System.out.println("*******************");
Function<Double,Long> func1 = d -> Math.round(d);
System.out.println(func1.apply(12.3));
System.out.println("*******************");
Function<Double,Long> func2 = Math::round;
System.out.println(func2.apply(12.6));
}
// 情况:类 :: 实例方法 (有难度)
// Comparator中的int comapre(T t1,T t2)
// String中的int t1.compareTo(t2)
@Test
public void test5() {
Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
System.out.println(com1.compare("abc","abd"));
System.out.println("*******************");
Comparator<String> com2 = String :: compareTo;
System.out.println(com2.compare("abd","abm"));
}
//BiPredicate中的boolean test(T t1, T t2);
//String中的boolean t1.equals(t2)
@Test
public void test6() {
BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
System.out.println(pre1.test("abc","abc"));
System.out.println("*******************");
BiPredicate<String,String> pre2 = String :: equals;
System.out.println(pre2.test("abc","abd"));
}
// Function中的R apply(T t)
// Employee中的String getName();
@Test
public void test7() {
Employee employee = new Employee(1001, "Jerry", 23, 6000);
Function<Employee,String> func1 = e -> e.getName();
System.out.println(func1.apply(employee));
System.out.println("*******************");
Function<Employee,String> func2 = Employee::getName;
System.out.println(func2.apply(employee));
}
构造器引用
格式: 类名 :: new
与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象方法的参数列表一致!且方法的返回值即为构造器对应类的对象
数组引用
**格式: ** type[] :: new
Function<Integer,String[]> func1 = (length) -> new String[length];
String[] arr1 = func1.apply(5);
//等价于
Function<Integer,String[]> func2 = String[] :: new;
String[] arr2 = func2.apply(10);
可参考文档
Stream API
为什么要使用Stream API
实际开发中,项目中多数数据源都来自于Mysql,Oracle等。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要Java层面去处理
Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据结构,而 Stream 是有关计算的。前者是主要面向内存,存储在内存中,后者主要是面向 CPU,通过 CPU 实现计算。
什么是 Stream
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
集合讲的是数据,Stream讲的是计算!
注意:
Stream 自己不会存储元素。
Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
Stream操作的三个步骤
- 创建Stream
- 中间操作
- 终止操作
创建Stream
-
Java8 中的 Collection 接口被扩展,提供了两个获取流的方法
-
default Stream<E> stream()
: 返回一个顺序流 -
default Stream<E> parallelStream()
: 返回一个并行流
-
-
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流
-
static <T> Stream<T> stream(T[] array)
* 返回一个流
-
通过Stream的of()
Stream 的中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为惰性求值。
1-筛选与切片
2-映射
3-排序
终止操作
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。
流进行了终止操作后,不能再次使用。
-
count()
返回流中元素总数 -
max(Comparator c)
返回流中最大值 -
min(Comparator c)
返回流中最小值 -
forEach(Consumer c)
内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)
归约和收集
-
reduce(T iden, BinaryOperator b)
可以将流中元素反复结合起来,得到一个值。返回 T -
collect(Collector c)
将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法