Java组件总目录
Java8 Stream 流编程
一、Lambda 表达式
Lambda表达式是JDK8中出现的新特性,其是函数接口的一种实现方式,用于代替匿名内部类。
函数式接口,Functional Interface,也称为功能性接口。简单来说,接口中可以包含多个方法,但仅能有一个自己的抽象方法,即接口的默认方法和静态方法并不影响一个接口成为函数式接口。
例如,Java标准库中的java.lang.Runnable是典型的函数式接口。
下面根据函数式接口中方法签名的不同,对 Lambda 表达式的用法进行分类。
@FunctionalInterface 注解的作用只是告诉编译器检查这个接口,保证该接口只能包含一个抽象方法,否则就会编译出错。
1 无参数无返回值
@FunctionalInterface
interface Some {
// 无参数无返回值
void doSome();
}
public class Test1 {
@Test
public void test01() {
/*匿名类表达式包含以下内部分
1 操作符:new;
2 一个要实现的接口或要继承的类,
3 一对括号,如果是匿名子类,与实例化普通类的语法类似,如果有构造参数,要带上构造参数;如果是实现一个接口,只需要一对空括号即可;
4 一段被"{}"括起来类声明主体;
5末尾的";"号(因为匿名类的声明是一个表达式,是语句的一部分,因此要以分号结尾)。
*/
Some some = new Some() {
@Override
public void doSome() {
System.out.println("使用匿名内部类实现");
}
};
some.doSome();
}
@Test
public void test02() {
Some some = () -> {
System.out.println("使用Lambda实现");
};
some.doSome();
}
}
2 有参数有返回值
@FunctionalInterface
interface Some3 {
// 有参数有返回值
String doSome(String a, int b);
}
public class Test3 {
@Test
public void test01() {
Some3 some3 = new Some3() {
@Override
public String doSome(String a, int b) {
return a + b;
}
};
System.out.println(some3.doSome("Hello,", 2019));
}
@Test
public void test02() {
Some3 some3 = (str, n) -> str + n;
System.out.println(some3.doSome("hello, ", 2020));
}
}
3 函数式接口与默认方法
只包含一个抽象方法的接口称为函数式接口,而接口中的默认方法并不是抽象方法,所以函数式接口中可以包含默认方法。即 Lambda 表达式可以实现包含有默认方法的函数式接口。
@FunctionalInterface
interface Some4 {
String doSome(String a, int b);
// 默认方法
default void doOther(String a, int b) {
System.out.println("执行默认方法 - " + a + b);
}
}
public class Test4 {
@Test
public void test01() {
// 调用抽象方法
Some4 some4 = (a, b) -> a + b;
System.out.println(some4.doSome("Hello, ", 2020));
// 调用默认方法
some4.doOther("hello ", 2019);
}
}
二、函数式接口编程
JDK8 中就根据接口方法参数与返回值的不同,定义好了若干内置的函数接口。在使用Lambda 表达式时,无需再定义那么多其它接口了,只需根据情况选择内置接口即可。
1 Predicate 接口
该接口用于判断输入的对象是否符合某个条件。该接口中只有一个抽象方法 test(),三个默认方法 and(与)、or(或)、negate(非),还有一个静态方法 isEqual()。
该方法会将参数值应用于接口 Lambda 表达式,测试该值的断言。IntPredicate、DoublePredicate 与 Predicate 接口没有任何关系,是另外一种全新的接口,仅仅是为了使用方便。
public class Test1 {
@Test
public void test01() {
Predicate<Integer> pre = i -> i > 8;
/*无关系,只是为了不写泛型,使用IntPredicate接口*/
IntPredicate intPre = i -> i < 3;
DoublePredicate doublePre = n -> n < 5;
/* test接受参数 */
System.out.println(pre.test(9)); // true
System.out.println(pre.test(7)); // false
System.out.println(intPre.test(9)); // false
System.out.println(intPre.test(2)); // true
}
@Test
public void test02() {
Predicate<Integer> gt8 = i -> i > 8;
Predicate<Integer> lt3 = i -> i < 3;
/*该接口提供了三个默认方法 and(与)、or(或)、negate(非),
它们用于对两个断言结果再次进行运算。*/
System.out.println(gt8.and(lt3).test(9)); // false
System.out.println(gt8.or(lt3).test(9)); // true
System.out.println(gt8.negate().test(9)); // false
}
@Test
public void test03() {
/*该方法的参数将作为比较对象,与 test()方法的参数进行相等性比较*/
System.out.println(Predicate.isEqual("Hello").test("hello")); // false
System.out.println(Predicate.isEqual("Hello").test("Hello")); // true
}
}
2 Consumer 接口
Consumer,消费者,只有一个输入没有输出的函数接口。该接口有一个抽象方法accept(),与一个默认方法 andThen()。
public class Test2 {
@Test
public void test01() {
Consumer<String> con = str -> System.out.println("Hello, " + str);
/*该方法用于将参数值应用于接口 Lambda 表达式*/
con.accept("Tom");
}
@Test
public void test02() {
Consumer<Integer> con1 = n -> System.out.println(n * 2);
Consumer<Integer> con2 = n -> System.out.println(n * n);
/*andThen,然后。andThen()方法将连接两个 Consumer 表达式,先执行前面的,再执行后面的。*/
con1.andThen(con2).accept(5);
}
}
3 Supplier 接口
Supplier,提供者,没有输入但有输出的函数接口。该接口只有一个抽象方法 get(),用于获取函数接口方法的返回值。
public class Test3 {
@Test
public void test01() {
Supplier<String> supp = () -> "Lambda";
System.out.println(supp.get());
}
}
4 Function 接口
只有一个输入,且有一个输出的函数接口。该接口中有一个抽象方法 apply(),有两个默认方法 andThen()与 compose(),及一个静态方法 identity()。
compose()方法的执行恰好与 andThen()的顺序相反。
public class Test4 {
@Test
public void test01() {
Function<Integer, String> fun = n -> "我爱你," + n;
/*该方法用于将参数值应用于接口方法。*/
System.out.println(fun.apply(2019));
}
@Test
public void test02() {
Function<Integer, Integer> fun1 = x -> x * 2;
Function<Integer, Integer> fun2 = x -> x * x;
// 先将5作为fun1的参数,计算结果为10,
// 再将fun1的计算结果10,作为fun2的参数再计算
System.out.println(fun1.andThen(fun2).apply(5)); // 100
// 先将5作为fun2的参数,计算结果为25,
// 再将fun2的计算结果25,作为fun1的参数再计算
System.out.println(fun1.compose(fun2).apply(5)); // 50
}
@Test
public void test03() {
/*Function 接口包含的静态方法,其返回结果为该 Function 的输入参数值*/
System.out.println(Function.identity().apply(5)); // 5
System.out.println(Function.identity().apply(3 * 8)); // 24
}
}
5 UnaryOperator 接口
同Function功能, 输入参数与结果类型一致。
6 BiFunction 接口
有两个输入与一个输出的函数接口。BiFunction 接口与 Function 接口没有任何关系。其有一个抽象方法 apply(),与一个默认方法 andThen()。这两个方法的意义与前面的相同。
7 BinaryOperator 接口
继承BiFunction 接口,增加minBy 获取最小值, maxBy,获取最大值,需要传入比较器。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private String name;
private int age;
}
// Student比较器
public class StudentComparator implements Comparator<Student> {
// 比较规则:年龄大者Student对象大
@Override
public int compare(Student s1, Student s2) {
if(s1.getAge() > s2.getAge()) {
return 1;
} else if(s1.getAge() < s2.getAge()) {
return -1;
}
return 0;
}
}
public class Test7 {
@Test
public void test03() {
Student s3 = new Student("张三", 23);
Student s4 = new Student("李四", 24);
StudentComparator studentComparator = new StudentComparator();
Student minStu = BinaryOperator.minBy(studentComparator).apply(s3, s4);
Student maxStu = BinaryOperator.maxBy(studentComparator).apply(s3, s4);
System.out.println(minStu); // s3
System.out.println(maxStu); // s4
}
}
三、Lambda 方法引用
Lambda 方法引用是指,借助内置函数式接口对一个类中的静态方法、实例方法、构造方法进行使用的方式。
Java 8 中还可以通过方法引用来表示 Lambda 表达式。方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法。Java 8 允许你通过"::"关键字获取方法或者构造函数的引用。方法引用提供了一种引用而不执行方法的方式,它需要由兼容的函数式接口构成目标类型上下文。计算时,方法引用会创建函数式接口的一个实例
// 定义 Person 类
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
// 静态方法
public static void sleeping(int hours) {
System.out.println("人们每天需要睡眠" + hours + "小时");
}
// 实例方法
// 在实例方法的第一个参数位置存在一个隐藏参数this
public String play(Person this, int minutes) {
// public String play(int minutes) {
return name + "已经玩儿了" + minutes + "分钟了。";
}
// 实例方法
public void study(Person this, String course) {
// public void study(String course) {
System.out.println(name + "正在学习" + course);
}
@Override
public String toString() {
return "Person{name=" + name + "}";
}
}
使用的方式示例
public class MethodTest {
@Test
public void test01() {
Person person = new Person("张三");
System.out.println(person.play(5));
person.study("webflux");
}
// Lambda静态方法引用 类名::静态方法名
@Test
public void test02() {
// sleeping()方法只有一个输入,没有输出,符合函数式接口Consumer的定义
Consumer<Integer> con = Person::sleeping;
con.accept(8); // 相当于 Person.sleeping(8)
}
// Lambda实例方法引用 实例名::实例方法名
@Test
public void test03() {
Person person = new Person("李四");
// play()方法只有一个输入,且有输出,符合函数式接口Function的定义
Function<Integer, String> fun = person::play;
System.out.println(fun.apply(5)); // 相当于person.play(5)
}
// Lambda实例方法引用 类名::实例方法名
@Test
public void test04() {
Person person = new Person("李四");
// play()方法只有一个输入,且有输出,符合函数式接口Function的定义
BiFunction<Person, Integer, String> bf = Person::play;
System.out.println(bf.apply(person, 5)); // 相当于person.play(5)
}
// Lambda实例方法引用 实例名::实例方法名
@Test
public void test05() {
Person person = new Person("李四");
// study()方法只有一个输入,没有输出,符合函数式接口Consumer的定义
Consumer<String> con = person::study;
con.accept("WebFlux"); // 相当于person.study("WebFlux")
}
// Lambda无参构造器引用 类名::new
@Test
public void test06() {
// 无参构造器是没有输入,但有输出,其符合函数式接口Supplier的定义
Supplier<Person> supp = Person::new;
System.out.println(supp.get()); // 相当于 new Person()
}
// Lambda带参构造器引用 类名::new
@Test
public void test07() {
// 带参构造器是仅有一个输入,且有输出,其符合函数式接口Function的定义
Function<String, Person> fun = Person::new;
System.out.println(fun.apply("王五")); // 相当于new Person("王五")
}
}