跟着官网的教程一起深入学习Lambda表达式:
Using Lambdas Expressions in Your Application - Dev.java
二、运用Lambda表达式到程序中
JDK8加了一个新包java.util.function,里面定义了很多函数式接口,主要分为4类:Supplier<T>、
Consumer<T>、Predicate<T>、Function<T, R>
1.想获取一个新对象使用Supplier<T>
Supplier没有入参,会返回一个对象,而且每次调用都会返回一个新的对象
@FunctionalInterface
public interface Supplier<T> {
T get();
}
下面是一个简单的Supplier实现如下:
public static void main(String[] args) {
Supplier<String> str = () -> "hello Duke";
System.out.println(str.get());
}
再看一个实现示例:
public static void main(String[] args) {
Random random = new Random(314L);
Supplier<Integer> supplier = () -> random.nextInt(10);
for (int i = 0; i < 5; i++) {
System.out.println(supplier.get());
}
}
上面的操作会两次装箱和拆箱操作,一个是random.nextInt(10)返回int时会自动装箱为Integer;另一个是调用get后获得随机数后(这里我自己也不是很懂,有人懂的赐教一下),这样的自动装箱拆箱会影响性能。考虑到这个问题,JDK提供了优化的方法供使用。比如提供了专门的IntSupplier<T>:
@FunctionalInterface
public interface IntSupplier {
int getAsInt();
}
用IntSupplier修改上面的例子:
Random random = new Random(314L);
IntSupplier newRandom = () -> random.nextInt();
for (int i = 0; i < 5; i++) {
int nextRandom = newRandom.getAsInt();
System.out.println("next random = " + nextRandom);
}
JDK提供了四个这样的专用Supplier:IntSupplier, BooleanSupplier, LongSupplier, DoubleSupplier.其中的get方法分别为getAsInt(),getAsBoolean(),getAsLong(),getAsDouble()
2.需要使用一个对象用Consumer<T>
Consumer和Supplier相反,它需要入参,但不返回东西,而且它除了抽象方法还有一个default方法
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
// default methods removed
}
在Supplier的例子中使用Consumer:
public static void main(String[] args) {
Random random = new Random(314L);
IntSupplier supplier = () -> random.nextInt(10);
Consumer<Integer> consumer = s -> System.out.println(s);
for (int i = 0; i < 5; i++) {
consumer.accept(supplier.getAsInt());
}
}
Consumer和Supplier一样,考虑到性能优化,提供了专门的IntConsumer,LongConsumer,DoubleConsumer,因为它们都没有返回值,方法均为accept()。
JDK还提供了一个接收两个参数的BigConsumer<T, U>
@FunctionalInterface
public interface BiConsumer<T, U> {
void accept(T t, U u);
// default methods removed
}
使用BigConsumer重写写获取随机数的例子:
public static void main(String[] args) {
BiConsumer<Random, Integer> consumer = (r, n) -> {
for (int j = 0; j < n; j++) {
System.out.println(r.nextInt(10));
}
};
consumer.accept(new Random(314L), 5);
}
BigConsumer也提供了专门的ObjIntConsumer,ObjLongConsumer,ObjDoubleConsumer
3.当需要判断一个对象是否满足某个条件时用Predicate<T>
predicate常常会被用来做过滤
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
// default and static methods removed
}
Predicate的基本类型专用接口有IntPredicate,LongPredicate,DoublePredicate,同时也有支持两个入参的接口BigPredicate<T, U>,但是BigPredicate没有基本类型的专用接口。
用Predicate对list做过滤:
public static void main(String[] args) {
List<String> list = List.of("one", "two", "three", "four", "five");
List<String> strings = new ArrayList<>(list);
Predicate<String> predicate = s -> s.length()%2==0;
strings.removeIf(predicate);
System.out.println(strings);
}
输出如下:
注意这里的removeIf()会修改调用列表的内容,所以像List.of()和Arrays.asList()生成的list,调用removeIf()时会运行保存,因为这两种方式生成的列表都是不可变的。
4.从一个类型映射到另一个类型用Function<T, R>
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
// default and static methods removed
}
例如:
Function<String, Integer> toLength = s -> s.length();
String word = ...; // any kind of word will do
int length = toLength.apply(word);