Java函数式编程(四)JDK8对库的增强
目录
3 函数式编程特性的应用
3.3 JDK8对库的增强
为了在Java中支持函数式编程,JDK除了增加上面列举的这些新的函数和Stream接口,类以外,还需要对既存的接口和类进行增强,例如给Collection接口增加stream()方法。变更接口带来的后果就是JDK8之前的程序在JDK8上不能构建,例如缺少stream()方法的实现。所以JDK8还要考虑向后兼容问题,体现在一下几个方面:
3.3.1 接口增加默认方法
接口增加默认方法解决了接口变更向后兼容的问题。例如:
为了支持Stream, Collection接口中增加了如下默认方法:
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
默认方法式支持多态的。
在多层类继承结构中,类中的默认方法的实现总是优先覆盖父类或接口中的实现.
3.3.2 接口的多重继承
接口是可以多重继承的,如果多个接口包含相同签名的默认方法,子类无法确定继承哪个,编译会报错。子类需要自己实现个个方法,在具体的实现中选择调用哪一个父类的实现。可以使用如下的增强的super语法:
public class child implements Interface1, Interface2 {
@Override
public void method1() {
Interface1.super.method1();
}
}
如何确定在多重继承的体系结构中,子类中的默认方法是哪一个?有如下3原则:
1.类胜于接口
2.子类胜于父类
3.如果以上两个原则不适用,那么子类需要自己实现,或者将该方法声明为抽象方法
3.3.3 接口的静态方法
JDK8建议将接口级别相关的方法放在接口中,也就是接口中的静态方法。
例如在Stream接口中工厂方法:
public static<T> Stream<T> of(T t) {
return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}
3.3.4 Optional类
之前看到,在Stream接口中,reduce方法有两个实现,其中一个的返回值为Optional类型
Optional<T> reduce(BinaryOperator<T> accumulator);
Optional是为和兴类库设计的一个新的数据类型,用来替换null值。
在上例中,如果Stream是空的,而且此接口并没有指定初始值,那么reduce方法并不会直接返回null,而是返回一个代表empty的Optional对象,用来驱使程序员检查返回值是否为空,从而避免了NullPointerException带来的弊端。
修饰符和返回值 | 方法 | 描述 |
---|---|---|
static Optional | empty() | 返回一个空的 Optional 实例。 |
boolean | equals(Object obj) | 指示其他对象是否“等于”此Optional对象. |
Optional | filter(Predicate<? super T> predicate) | 如果存在一个值,并且该值与给定的谓词匹配,则返回一个描述该值的 Optional,否则返回一个空的 Optional。 |
Optional | map(Function<? super T,? extends U> mapper) | 如果存在值,则将提供的映射函数应用于该值,如果结果为非空,则返回描述结果的Optional对象。 |
Optional | flatMap(Function<? super T,Optional> mapper) | 如果存在值,则对其应用提供的 Optional-bearing 映射函数,返回该结果,否则返回空 Optional.此方法类似于 map(Function),但提供的映射器是其结果已经是 Optional 的映射器,如果被调用,flatMap 不会用附加的 Optional 包装它。 |
T | get() | 如果此 Optional 中存在值,则返回该值,否则抛出 NoSuchElementException。 |
int | hashCode() | 返回当前值的哈希码值(如果有),如果没有值,则返回 0(零)。 |
void | ifPresent(Consumer<? super T> consumer) | 如果存在值,则使用该值调用指定的消费者,否则什么也不做。 |
boolean | isPresent() | 如果存在值则返回真,否则返回假。 |
static Optional | of(T value) | 返回具有指定当前非空值的 Optional。 |
static Optional | ofNullable(T value) | 返回一个描述指定值的Optional,如果非null,否则返回一个空Optional。 |
T | orElse(T other) | 如果存在则返回值,否则返回other。 |
T | orElseGet(Supplier<? extends T> other) | 如果存在则返回该值,否则调用 other 并返回该调用的结果。 |
T | orElseThrow(Supplier<? extends X> exceptionSupplier) | 返回包含的值,如果存在,否则抛出由提供的Supplier创建的异常。 |
String | toString() | 返回此 Optional 适合调试的非空字符串表示形式。 |
Optional示例:
package com.qupeng.fp.lambda;
import java.util.NoSuchElementException;
import java.util.Optional;
public class TestOptional {
public static void main(String[] args) {
// 工厂方法1:empty
Optional<String> optionalStr = Optional.empty();
if (!optionalStr.isPresent()) {
try {
optionalStr.get();
} catch (NoSuchElementException elementException) {
System.out.println(elementException);
}
System.out.println(optionalStr.orElse("String from orElse method."));
System.out.println(optionalStr.orElseGet(() -> "String from orElseGet method."));
}
// 工厂方法2: ofNullable
optionalStr = Optional.ofNullable(null);
try {
optionalStr.orElseThrow(RuntimeException::new);
} catch (RuntimeException exception) {
System.out.println(exception);
}
// 工厂方法3: of
try {
optionalStr = Optional.of(null);
} catch (NullPointerException exception) {
System.out.println(exception);
}
// 工厂方法3: of
optionalStr = Optional.of("It's a optional String.");
optionalStr.ifPresent(System.out::println);
// map方法
Optional<Integer> optionalInt = optionalStr.map(str -> {
try {
return Integer.valueOf(str);
} catch (NumberFormatException exception) {
return null;
}
});
if (!optionalInt.isPresent()) {
System.out.println("It's not a integer.");
}
// flatMap方法
optionalStr = Optional.of("99");
optionalInt = optionalStr.flatMap(str -> {
try {
return Optional.of(Integer.valueOf(str));
} catch (NumberFormatException exception) {
return Optional.empty();
}
});
if (optionalInt.isPresent()) {
System.out.println(optionalInt.get());
}
// filter方法
optionalInt.filter(integer -> integer > 0);
if (optionalInt.isPresent()) {
System.out.println(optionalInt.get());
} else {
System.out.println("The number in Optional is greater than 99");
}
}
}
OptionalInt
OptionalLong
OptionalDouble
3.3.5 方法引用
方法引用是Lambda表达式一种简写的方式,通过方法的名字来指向一个方法,使用一对冒号 :: 作为运算符。例如:Consumer consumer = System.out::println。
任何使用Lambda表达是的地方都可以使用方法引用替代,它可以使语言的构造更紧凑简洁,减少冗余代码。
方法引用有四种基本方式:
构造器引用:
- 它的语法是Class::new,或者更一般的Class::new实例如下:
Supplier<String> supplier = String::new;
- 静态方法引用:它的语法是Class::static_method,实例如下:
IntFunction<String> intFunction = Integer::toString;
- 特定类的任意对象的方法引用:它的语法是Class::method实例如下:
Consumer<Integer> action = Integer::doubleValue;
这种情况比较特殊,函数接口的形参列表和被引用方法的形参列表不是完全一致的。函数接口的第一个形参对应调用类实例方法的那个对象,所以函数接口的形参列表比类实例方法多第一个参数。
void accept(T t); // t就是调用方法引用的那个对象
public abstract double doubleValue(); // 实际的调用可以看作:t.doubleValue()
- 特定对象的方法引用:它的语法是instance::method实例如下:
Integer int99 = Integer.valueOf(99);
IntFunction<Integer> intFunction2 = int99::compareTo;
简答的示例:
package com.qupeng.fp.lambda;
import java.util.Arrays;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.IntFunction;
import java.util.function.Supplier;
public class TestMethodRef {
public static void main(String[] args) {
// 构造函数引用
Supplier<String> supplier = String::new;
String helloWorld = supplier.get();
System.out.println(helloWorld);
// 静态方法引用
IntFunction<String> intFunction = Integer::toString;
String str = intFunction.apply(99);
System.out.println(str);
// 特定类的任意对象的方法引用1
Consumer<Integer> action = Integer::doubleValue;
Arrays.asList(1, 2, 3).forEach(action);
// 特定类的任意对象的方法引用2
BiFunction<Integer, Integer, Integer> biFunction = Integer::compareTo;
int result = biFunction.apply(100, 99);
System.out.println(result);
// 特定对象的方法引用
Integer int99 = Integer.valueOf(99);
IntFunction<Integer> intFunction2 = int99::compareTo;
result = intFunction2.apply(100);
System.out.println(result);
}
}
3.3.6 Map
JDK 8中为Map接口新增了三个默认方法,三个高阶函数。用来扩展获取元素时的一些行为,例如取不到时重新计算:
default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
简单的示例:
package com.qupeng.fp.lambda;
import java.util.HashMap;
import java.util.Map;
public class TestMap {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap();
map.put("a", 2);
map.put("b", 0);
map.put("c", 3);
// 不管value是否存在,都重新计算一个值
Integer value1 = map.compute("a", (str, integer) -> {
if (null == integer || 1 != integer) {
return Integer.valueOf(1);
} else {
return integer;
}
});
// value不存在,计算一个新值
Integer value2 = map.computeIfAbsent("b", s -> {
if (s.equals("b")) {
return 2;
} else {
return 0;
}
});
// value存在,递增当前value
Integer value3 = map.computeIfPresent("c", (str, integer) -> ++integer);
System.out.println("" + value1 + ", " + value2 + ", " + value3);
System.out.println(map);
}
}