前言
本篇博客主要分享Java8的新特性,O(∩_∩)O哈哈~
Lambda表达式总结
Lambda表达式是Java 8中添加的一个新特性。它可以让我们的代码更加简洁紧凑。
Lambda表达式的基本格式是:
(parameters) -> expression
或者
(parameters) -> { statements; }
基本规则:
- 参数的数据类型可以省略,编译器可以推导出来
- 如果只有一个参数,圆括号()也可以省略
- 表达式或语句块的{}也可以省略,前提是语句只有一行
- Lambda表达式主要用来定义行内执行的方法类型接口,例如Runnable、Comparator等
例子:// 之前的匿名内部类 new Thread(new Runnable() { public void run() { System.out.println("Hello"); } }).start(); // 使用Lambda表达式 new Thread(() -> System.out.println("Hello")).start(); // Comparator接口 Comparator<Integer> c = (a, b) -> a - b; // 如果只有一个参数,圆括号可以省略 Comparator<Integer> c = a -> a * 2; // 代码块如果只有一行,可以省略花括号和return关键字 Comparator<Integer> c = (a, b) -> a - b; // 有多个语句,需要花括号和return Comparator<Integer> c = (a, b) -> { int result = a - b; return result; };
函数式接口 + Lamda表达式推导过程
函数式接口:是一个只包含一个抽象方法的接口,这种接口可以通过Lambda表达式来实例化。Java 8中有许多内置的函数式接口,比如:
- Runnable
- Comparator
- Predicate
- Consumer
- Supplier
- Function
这些接口只包含一个抽象方法。
Lambda表达式的推导过程:- 确定目标接口的抽象方法。Lambda表达式需要实现的接口,通常是一个函数式接口。
- 抽象方法的参数列表肯定就是Lambda表达式的参数列表。
- 确认Lambda表达式的主体依赖于抽象方法的返回值:
- 如果返回值为void,Lambda表达式的主体可以是一条语句或语句块。
- 如果返回值不是void,主体应该是一个表达式。该表达式的类型应与返回值类型兼容。
- 如果返回值不是void,但是存在参数,Lambda表达式不需要返回值,也可以使用语句块。
- 确认参数的数据类型:
- 如果参数的数据类型可以从上下文推导出来,那么可以省略其数据类型。
- 否则必须定义全类型。
- 移除抽象方法的modifiers,异常和返回值:在转换为Lambda表达式时忽略抽象方法的修饰符、异常和返回值。
例如,对于 Runnable 接口:@FunctionalInterface public interface Runnable { public abstract void run(); }
对应的Lambda表达式为:
() -> System.out.println("Hello")
推导过程:
- 确定Runnable接口的抽象方法run(),没有参数和返回值。
- 所以Lambda表达式的参数列表为空。
- 由于run()方法返回void,所以Lambda表达式的主体可以是一条语句或语句块。
- 不需要定义参数的数据类型。
- 移除public和abstract关键字,不考虑异常和返回值。
所以对应得出的Lambda表达式是() -> System.out.println("Hello")。Supplier简介
Supplier接口是一个函数式接口,它只包含一个无参的get()方法。该方法返回一个任意泛型类型的值。
Supplier接口用于表示一个提供值的源头,允许一个方法在没有输入参数的情况下产生一个值。
Supplier接口的定义如下:@FunctionalInterface public interface Supplier<T> { T get(); }
例子:
// 不使用Supplier,普通方法 public String getHello() { return "Hello"; } // 使用Supplier接口 + Lambda表达式 Supplier<String> supplier = () -> "Hello"; String result = supplier.get(); // Returns "Hello"
Supplier接口常用在需要一个方法来生成某个值时,如工厂方法等。例如:
Supplier<Person> personSupplier = Person::new; // 使用方法引用简化Lambda表达式 Person p = personSupplier.get();
这在需要一个随机数或当前时间戳时也很有用:
Supplier<Integer> randomInt = () -> (int) (Math.random() * 100D); int num = randomInt.get(); Supplier<Long> timestamp = () -> System.currentTimeMillis(); long now = timestamp.get();
所以,在需要一个不需要输入参数,但可以提供一个值的方法时,可以考虑使用Supplier函数式接口,从而使用Lambda表达式简化代码。
总结:
Supplier接口用于表示一个提供值的方法,它允许一个方法在没有输入参数的情况下产生一个值。
常用于工厂方法、随机值或时间戳的生成等。可以和Lambda表达式配合使用简化代码。Optional
Optional是一个容器类,它可能包含非空值,也可能为空。它可以避免NullPointerException。
Optional类的三个基本操作:
- 移除抽象方法的modifiers,异常和返回值:在转换为Lambda表达式时忽略抽象方法的修饰符、异常和返回值。
- Optional.of(T value): 创建一个Optional实例,value必须非空;
- Optional.empty(): 创建一个空的Optional实例;
- Optional.ofNullable(T value): value可以为null,创建Optional实例;
Optional类提供很多有用的方法,如: - isPresent(): 判断是否包含值;
- get(): 获取值,若 calling get() on an empty Optional抛出NoSuchElementException;
- orElse(T other): 获取Optional包含的值,或返回other的值;
- orElseGet(Supplier<? extends T> other): 获取Optional包含的值,或调用Supplier接口返回的值;
- map(Function<? super T,? extends U> mapper): 如果有值,则对其执行mapping函数并返回Optional,否则返回Optional.empty();
- flatMap(Function<? super T,Optional> mapper): 与map()类似,但mapping函数返回Optional的值;
- filter(Predicate<? super T> predicate): 如果Optional有值且使predicate返回true,则保留,否则返回Optional.empty();
示例:// of()方法 Optional<Integer> optional = Optional.of(5); // empty() 方法 Optional<Integer> optional = Optional.empty(); // ofNullable() 方法 Optional<Integer> optional = Optional.ofNullable(null); // isPresent()方法 optional.isPresent(); // Returns true if optional contains a value // get()方法 optional.get(); // Returns the value, throws NoSuchElementException if empty // orElse()方法 optional.orElse(10); // Returns 10 if optional is empty, otherwise returns the value // map()方法 optional.map(x -> 2 * x); // Returns Optional[10] if optional contains 5, otherwise empty // flatMap()方法 Optional<Integer> x = Optional.of(5); Optional<Double> y = x.flatMap(a -> Optional.of(a * 2.0)); // Returns Optional[10.0] // filter()方法 Optional<Integer> x = Optional.of(5); x.filter(a -> a > 1); // Returns Optional[5] x.filter(a -> a > 10); // Returns Optional.empty
Optional类是一个很好的容器,可以避免空指针异常,并且提供 many有用的方法操作Optional实例。在Java 8以前一般使用null来表示一个缺失的值,现在推荐使用Optional代替null。
Java8中最重磅的升级Stream
Java 8中最大的升级就是Stream API。Stream是一个来自数据源的元素队列,它支持聚合操作,汇总统计,过滤,映射等多种操作。
Stream API提供了一种高效且易用的处理数据的方式。主要特征如下: - 不会改变原始数据源,会返回一个持有结果的新Stream。
- 惰性求值:许多Stream操作是向后延迟的,意味着他们会等到需要结果的时候才执行。
- 可消费性:Stream只能被“消费”一次,一旦遍历过就会被关闭,就像一个Iterator。
- 内部迭代:Stream使用内部迭代来遍历元素,而不是像集合的外部迭代。
- 可并行化:当一个Stream是并行的,许多Stream操作可以自动并行化执行。
Stream的构成主要分为三部分:- 数据源:可以从Collection、数组等构建Stream。
- 中间操作:一步一步构建我们的Stream,并且可以被链式调用。主要包含:
- filter - 过滤掉某些元素
- map - 转换每个元素
- flatMap - 把Stream中的每个元素转换成另一个Stream,然后把所有的Stream连接起来
- distinct - 去除重复的元素
- sorted - 对Stream进行排序
- peek - 对每个元素执行操作,主要用于调试
- limit - 截取前n个元素
- skip - 跳过前n个元素
- 终止操作:产生结果、或导致副作用的操作。主要包含:
- allMatch、anyMatch、noneMatch - 检查是否匹配全部、任意、没有元素
- findFirst、findAny - 返回第一个或任意一个元素
- 终止操作:产生结果、或导致副作用的操作。主要包含:
- skip - 跳过前n个元素
- count - 返回元素总数
- reduce - 把Stream中的元素组合起来
- collect - 把Stream中的元素归集到一个Collection中
- forEach - 对每个元素执行操作
- min、max - 返回最小值或最大值
- toArray - 把Stream中的元素转成数组
Stream执行顺序是:首先使用一个数据源构造Stream,然后进行0个或多个中间操作,最后进行一个终止操作。在结束操作时,Stream就被使用“消费”掉了,无法再次被操作。
这就是Java 8中引入的Stream的基本概念和用法。
它大大提高了Java的表达能力,可以快速对集合等进行过滤、映射、分组、汇总等操作。函数式接口Predicate
Predicate是一个函数式接口,它包含一个抽象方法test()。该方法接受一个泛型参数,并返回一个boolean。
Predicate接口用于表示一个断言(判断)方法,可以对任何输入参数进行判断并返回true或false结果。
Predicate接口的定义如下:@FunctionalInterface public interface Predicate<T> { boolean test(T t); }
例子:
// 普通方法实现 public boolean isAdult(int age) { return age > 18; } // 使用Predicate接口 + Lambda表达式 Predicate<Integer> predicate = age -> age > 18; boolean result = predicate.test(20); // Returns true
Predicate接口常用在过滤器、判断逻辑等场景。例如:
List<Person> persons = Arrays.asList( new Person("John", 20), new Person("Sara", 22), new Person("Mark", 15) ); // 过滤成年人 List<Person> adults = filter(persons, p -> p.getAge() > 18); // predicate方法参数使用方法引用 Person::getAge List<Person> adults = filter(persons, Person::getAge); // Predicate接口作为方法的参数 public List<Person> filter(List<Person> persons, Predicate<Person> predicate) { return persons.stream() .filter(predicate) .collect(Collectors.toList()); }
所以,Predicate接口主要用于表达一个boolean值的判断逻辑,它和Lambada表达式以及方法引用配合使用,可以让我们的代码更加简洁。
总结:
Predicate接口表示一个断言(判断)方法,包含一个抽象方法test()用来判断输入参数并返回boolean值。
常用于过滤器,判断逻辑等场景,可以和Lambda表达式及方法引用配合使用。自定义函数式接口
我们也可以自定义自己的函数式接口。自定义函数式接口需要遵循一定的规则:
- 必须是接口,且只能包含一个抽象方法。
- 可选地提供@FunctionalInterface注解,这个注解可以检查接口是否真的只包含一个抽象方法。如果违反了规则编译器会报错。
- 可以包含default方法和静态方法。
例子:自定义一个字符串过滤器接口@FunctionalInterface public interface StringPredicate { boolean test(String str); }
使用自定义接口:
StringPredicate predicate = str -> str.length() > 5; boolean result = predicate.test("Hello"); // Returns true
我们也可以作为方法参数使用:
public boolean filter(List<String> list, StringPredicate predicate) { return list.stream().anyMatch(predicate); }
调用方法:
List<String> list = Arrays.asList("Apple", "Banana", "Orange"); boolean result = filter(list, str -> str.startsWith("B")); // Returns true
当然,函数式接口不一定要使用Lambda表达式,也可以使用匿名内部类的方式实现:
StringPredicate predicate = new StringPredicate() { @Override public boolean test(String str) { return str.length() > 5; } };
但是Lambda表达式的方式无疑更加简洁。
所以,自定义函数式接口需要遵循: - 必须只包含一个抽象方法
- 可以使用@FunctionalInterface注解检查
- 可以包含default和静态方法
- 既可以通过Lambda表达式实现,也可以通过匿名内部类实现
自定义函数式接口增加了程序的扩展性,让我们可以根据自己的需求定义特定的函数式接口。
- toArray - 把Stream中的元素转成数组