文章主要参考尚硅谷
Java 8新特性简介
速度更快
代码更少(增加了新的语法 Lambda 表达式)
强大的 Stream API
便于并行
最大化减少空指针异常 Optional
其中最为核心的为 Lambda 表达式与Stream API
1. Lambda 表达式
1.1 为什么使用 Lambda 表达式
Lambda 是一个匿名函数,我们可以把 Lambda
表达式理解为是一段可以传递的代码(将代码
像数据一样进行传递)。可以写出更简洁、更
灵活的代码。作为一种更紧凑的代码风格,使
Java的语言表达能力得到了提升。
1.2 Lambda 表达式语法
Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “->” , 该操作符被称为 Lambda 操作符或剪头操作符。它将 Lambda 分为两个部分:
左侧:指定了 Lambda 表达式需要的所有参数
右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。
1.3 类型推断
上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”.
2. 函数式接口
2.1 什么是函数式接口
- 只包含一个抽象方法的接口,称为函数式接口。
- 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
- 我们可以在任意函数式接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
2.2 自定义函数式接口
作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。
2.3 Java 内置四大核心函数式接口
public class TestLambda {
List<Employee> emps = Arrays.asList(
new Employee(101, "张三", 18, 9999.99),
new Employee(102, "李四", 59, 6666.66),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "赵六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
@Test
public void test1(){
Collections.sort(emps, (e1, e2) -> {
if(e1.getAge() == e2.getAge()){
return e1.getName().compareTo(e2.getName());
}else{
return -Integer.compare(e1.getAge(), e2.getAge());
}
});
for (Employee emp : emps) {
System.out.println(emp);
}
}
@Test
public void test2(){
String trimStr = strHandler("\t\t\t 威武 ", (str) -> str.trim());
System.out.println(trimStr);
String upper = strHandler("abcdef", (str) -> str.toUpperCase());
System.out.println(upper);
String newStr = strHandler("威武", (str) -> str.substring(2, 5));
System.out.println(newStr);
}
//需求:用于处理字符串
public String strHandler(String str, MyFunction mf){
return mf.getValue(str);
}
@Test
public void test3(){
op(100L, 200L, (x, y) -> x + y);
op(100L, 200L, (x, y) -> x * y);
}
//需求:对于两个 Long 型数据进行处理
public void op(Long l1, Long l2, MyFunction2<Long, Long> mf){
System.out.println(mf.getValue(l1, l2));
}
}
/*
* Java8 内置的四大核心函数式接口
*
* Consumer<T> : 消费型接口
* void accept(T t);
*
* Supplier<T> : 供给型接口
* T get();
*
* Function<T, R> : 函数型接口
* R apply(T t);
*
* Predicate<T> : 断言型接口
* boolean test(T t);
*
*/
public class TestLambda3 {
//Predicate<T> 断言型接口:
@Test
public void test4(){
List<String> list = Arrays.asList("Hello", "Lambda", "www", "ok");
List<String> strList = filterStr(list, (s) -> s.length() > 3);
for (String str : strList) {
System.out.println(str);
}
}
//需求:将满足条件的字符串,放入集合中
public List<String> filterStr(List<String> list, Predicate<String> pre){
List<String> strList = new ArrayList<>();
for (String str : list) {
if(pre.test(str)){
strList.add(str);
}
}
return strList;
}
//Function<T, R> 函数型接口:
@Test
public void test3(){
String newStr = strHandler("\t\t\t 威武 ", (str) -> str.trim());
System.out.println(newStr);
String subStr = strHandler("威武", (str) -> str.substring(2, 5));
System.out.println(subStr);
}
//需求:用于处理字符串
public String strHandler(String str, Function<String, String> fun){
return fun.apply(str);
}
//Supplier<T> 供给型接口 :
@Test
public void test2(){
List<Integer> numList = getNumList(10, () -> (int)(Math.random() * 100));
for (Integer num : numList) {
System.out.println(num);
}
}
//需求:产生指定个数的整数,并放入集合中
public List<Integer> getNumList(int num, Supplier<Integer> sup){
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
Integer n = sup.get();
list.add(n);
}
return list;
}
//Consumer<T> 消费型接口 :
@Test
public void test1(){
happy(10000, (m) -> System.out.println("消费:" + m + "元"));
}
public void happy(double money, Consumer<Double> con){
con.accept(money);
}
}
2.4 练习
public static void main(String[] args) {
String str = "\t\t\tHello World ";
System.out.println("str = " + str);
System.out.println(handleString(str, s -> s.substring(0, 5)));
System.out.println(handleString(str, s -> s.trim()));
System.out.println(handleString(str, s -> s.toLowerCase()));
}
/**
* 字符串处理
*
* @param str
* @param function1
* @return
*/
public static String handleString(String str, MyFunction1 function1) {
return function1.handlerString(str);
}
// 2. 根据学生的成绩从小到大排序, 如果成绩一样, 则按照年龄从小到大排序.
Student[] students = {
new Student(1l, 18, 99d),
new Student(1l, 17, 99d),
new Student(1l, 18, 69d)
};
List<Student> list = Arrays.asList(students);
System.out.println("list = " + list);
Collections.sort(list, (s1, s2) -> {
int substract = Double.compare(s1.getExamResult(), s2.getExamResult());
if (substract == 0) {
return Integer.compare(s1.getAge(), s2.getAge());
}
return substract;
});
System.out.println("list = " + list);
// 3. 计算2个long类型数据差和乘积
System.out.println(operation(1D, 3D, (MyFunction2<Double>) (i1, i2) -> i1 + i2));
public static String handleString(String str, MyFunction1 function1) {
return function1.handlerString(str);
}
3. 方法引用与构造器引用
3.1 方法引用
什么时候使用方法引用?
当Lambda体的操作,已经有实现的方法了,可以使用方法引用!
三种主要使用情况:
- 对象::实例方法
- 类::静态方法
- 类::实例方法
方法引用注意事项
- 实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!
- 方法引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来。
- 如果我们引用的方法的第一个参数是调用对象, 而且使用 ClassName::methodName这种方式调用的时候, 那么我们的抽象方法的参数需要比引用方法的参数多一个。
举栗
BinaryOperator<Integer> binaryOperator = (x, y) -> Math.max(x, y);
System.out.println(binaryOperator.apply(1, 3));
public static void main(String[] args) {
accept("Method Reference!!!", x -> System.out.println(x));
accept("Method Reference!!!", System.out::println);
}
private static void accept(Object obj, Consumer consumer) {
consumer.accept(obj);
}
对象名::实例方法名
PrintStream out = System.out;
Consumer<Object> consumer = x -> out.println(x);
consumer.accept("Method Reference");
consumer = out::println;
consumer.accept("Hi");
consumer = System.out::println;
consumer.accept("Perfect");
Student student = new Student(1L, 18, 99.0);
// Supplier<Double> supplier = () -> student.getExamResult();
Supplier<Double> supplier = student::getExamResult;
System.out.println(supplier.get());
类名 :: 静态方法名
Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);
System.out.println(comparator.compare(1, 1));
类名::实例方法名
BiPredicate<String, String> biPredicate = (x, y) -> x.equals(y);
System.out.println(biPredicate.test("one", "one"));
biPredicate = String::equals;
System.out.println(biPredicate.test("two", "one"));
3.2 构造器引用
格式: ClassName::new
构造器参数列表要与接口中抽象方法的参数列表一致!
Supplier<Student> supplier = () -> new Student();
System.out.println(supplier.get());
Function<Long, Student> function = (x) -> new Student(x);
System.out.println(function.apply(1L));
Supplier<Student> supplier = Student::new;
System.out.println(supplier.get());
Function<Long, Student> function = Student::new;
System.out.println(function.apply(1L));
3.3 数组引用
格式: type[] :: new
Supplier<int[]> supplier = () -> new int[1];
System.out.println(supplier.get().length);
Function<Integer, Student[]> function = len -> new Student[len];
System.out.println(function.apply(10).length);
Function<Integer, Student[]> function = Student[]::new;
System.out.println(function.apply(10).length);
Comparator<Integer> comparator = Integer::compare;
System.out.println(comparator.compare(1, 1));