Java8实战Lambda表达式

Posted 7788it

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java8实战Lambda表达式相关的知识,希望对你有一定的参考价值。

在上一节中,我们为了使用Lambda表达式不得不创建了各种函数描述符的函数式接口,其实Java 8已经给我们提供了一套能够描述常见函数描述符的函数式接口。比如Predicate<T>Consumer<T>Function<T,R>Supplier<T>等,这些函数式接口位于java.util.function包。这一节主要记录这些函数式接口的应用。

Java8中的函数式接口

下表列出了Java8中常见的函数式接口:

函数式接口函数描述符原始类型特化
Predicate T->boolean IntPredicate,LongPredicate, DoublePredicate
Consumer T->void IntConsumer,LongConsumer, DoubleConsumer
Function<T,R> T->R IntFunction, IntToDoubleFunction, IntToLongFunction, LongFunction, LongToDoubleFunction, LongToIntFunction, DoubleFunction, ToIntFunction, ToDoubleFunction, ToLongFunction
Supplier ()->T BooleanSupplier,IntSupplier, LongSupplier, DoubleSupplier
UnaryOperator T->T IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator
BinaryOperator (T,T)->T IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator
BiPredicate<L,R> (L,R)->boolean  
BiConsumer<T,U> (T,U)->void ObjIntConsumer, ObjLongConsumer, ObjDoubleConsumer
BiFunction<T,U,R> (T,U)->R ToIntBiFunction<T,U>, ToLongBiFunction<T,U>, ToDoubleBiFunction<T,U>

Predicate

predicate: 英 [?pred?k?t] 美 [?pred?k?t] 断言,断定的意思。从接口的名称就可以推断出这个函数式接口的主要作用就是用于判断作用,Predicate源码如下所示:

@FunctionalInterface
public interface Predicate<T>
  boolean test(T t);
?
  default Predicate<T> and(Predicate<? super T> other)
      Objects.requireNonNull(other);
      return (t) -> test(t) && other.test(t);
 
  default Predicate<T> negate()
      return (t) -> !test(t);
 
  default Predicate<T> or(Predicate<? super T> other)
      Objects.requireNonNull(other);
      return (t) -> test(t) || other.test(t);
 
  static <T> Predicate<T> isEqual(Object targetRef)
      return (null == targetRef)
              ? Objects::isNull
              : object -> targetRef.equals(object);
 

 

可看到java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean,函数描述符为(T) -> boolean举几个例子:

// 偶数判断
Predicate<Integer> isEven = (in) -> in % 2 == 0;
isEven.test(17); // false
?
// 判断字符串的长度是否为0
Predicate<String> isEmptyString = String::isEmpty;
isEmptyString.test(""); // true

 

除了抽象方法外,java.util.function.Predicate<T>接口还定义了三个默认方法:andnegateor,对应“与”,“非”和“或”操作,这样我们便可以复合Lambda表达式了,比如:

// 判断是偶数,并且大于30
Predicate<Integer> isEven = (in) -> in % 2 == 0;
isEven.and((in) -> in > 30).test(40); // true
?
// 奇数判断
Predicate<Integer> isEven = (in) -> in % 2 == 0;
Predicate<Integer> isOdd = isEven.negate();
isOdd.test(17); // true

 

Consumer

英 [k?n?sju:m?(r)] 美 [k?n?su:m?(r)] n.消费者。该函数式接口用于消费一个对象,即接收一个对象,对其执行某些操作,然后没有返回值。Consumer源码如下所示:

@FunctionalInterface
public interface Consumer<T>
  void accept(T t);
  default Consumer<T> andThen(Consumer<? super T> after)
      Objects.requireNonNull(after);
      return (T t) -> accept(t); after.accept(t); ;
 

 

可看到java.util.function.Consumer<T>定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void),函数描述符为(T) -> void。其还提供了一个默认方法andThen。举个例子:

Consumer<Apple> printAppleColor = (a)-> System.out.println(a.getColor());
printAppleColor.accept(new Apple("red",17)); // red
?
printAppleColor.andThen((a) -> System.out.println(a.getWeight())).accept(new Apple("red", 17)); // red 17.0

 

Supplier

supplier 英 [s??pla??(r)] 美 [s??pla??r] n.供应商;供应者;供给者。其源码如下:

@FunctionalInterface
public interface Supplier<T>
  T get();

 

可看到java.util.function.Supplier<T>定义了一个名叫get的抽象方法,它不接收参数,返回泛型T的对象,函数描述符为() -> T。举个例子:

Supplier<Person> personSupplier = Person::new;
personSupplier.get();   // new Person

 

Functions

Functions源码如下:

@FunctionalInterface
public interface Function<T, R>
  R apply(T t);
?
  default <V> Function<V, R> compose(Function<? super V, ? extends T> before)
      Objects.requireNonNull(before);
      return (V v) -> apply(before.apply(v));
 
?
  default <V> Function<T, V> andThen(Function<? super R, ? extends V> after)
      Objects.requireNonNull(after);
      return (T t) -> after.apply(apply(t));
 
?
  static <T> Function<T, T> identity()
      return t -> t;
 

 

java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象,函数描述符为(T) -> R。举个例子:

Function<Apple, Double> getAppleWeight = (a) -> 
  return a.getWeight();
;
getAppleWeight.apply(new Apple(17)); // 17.0

 

Functions接口还提供了两个抽象方法composeandThen,从源码可以看出两者的根本区别。举个compose例子:

Function<Integer, Integer> f = (x) -> x + 1;
Function<Integer, Integer> g = (x) -> x * 2;
f.compose(g).apply(2); // 5

 

过程为:f(g(2)),也就是1+(2*2)

举个andThen的例子:

Function<Integer, Integer> f = (x) -> x + 1;
Function<Integer, Integer> g = (x) -> x * 2;
f.andThen(g).apply(2); // 6

 

过程为:g(f(2)),也就是(2+1)*2

原始类型特化

在学习Function接口的时候,我们定义了f函数:

Function<Integer, Integer> f = (x) -> x + 1;

 

x的类型为Integer类型,1为int类型,返回值为Integer类型,整个过程实际上为Integer.valueOf(x.intValue() + 1)。虽然编译器可以自动帮我们完成拆装箱,但这会造成不必要的性能消耗。考虑到了这一点,Java8为我们提供了int类型的Function接口:IntFunction:

@FunctionalInterface
public interface IntFunction<R>
  R apply(int value);

 

所以f最好重构为:

IntFunction<Integer> f = (x) -> x + 1;

 

剩余的原始类型特化函数式接口可参考上面的表格。

Java8中增强的Comparator

在Java8之前,Comparator接口用于实现简单的比较排序算法。比如有如下List:

List<Double> list = new ArrayList<>();
list.add(12.3);
list.add(100.2);
list.add(3.14);
list.add(27.7);
list.add(-9.8);

 

使用Comparator接口对其从小到大排序:

Collections.sort(list, new Comparator<Double>() 
  @Override
  public int compare(Double o1, Double o2)
      return o1.compareTo(o2);
 
);

 

Comparator接口也是一个函数式接口,函数描述符为(T,T) -> int,Java8中可以使用Lambda改造上面的排序方法:

Collections.sort(list, (o1, o2) -> o1.compareTo(o2));

 

Java8对List提供了sort方法,可以替代Collections.sort,所以上面的代码可以简化为:

list.sort((o1, o2) -> o1.compareTo(o2));

 

使用方法的引用来进一步简化:

list.sort(Double::compareTo);

 

Java8对Comparator进行了增强,加入了一些实用的默认方法,比如对排序结果反转:

Comparator<Double> comparator = Double::compareTo;
list.sort(comparator.reversed());

 

更多方法可以参考Comparator接口的JavaDoc。

查看Comparator的时候发现其虽然是函数式接口,但是却包含了compareequals这两个抽象方法,顿时有点懵逼,函数式接口不是只能有一个抽象方法么?查找资料后发现:函数式接口中可以额外定义多个抽象方法,但这些抽象方法签名必须和Object的public方法一样,接口最终有确定的类实现,而类的最终父类是Object。因此函数式接口可以定义Object的public方法。

以上是关于Java8实战Lambda表达式的主要内容,如果未能解决你的问题,请参考以下文章

Java8实战Lambda表达式

《Java8实战》读书笔记07:Lambda 重构测试和调试(设计模式实现)

《Java8实战》读书笔记07:Lambda 重构测试和调试(设计模式实现)

Java8实战之Stream

Java8学习笔记 - 在哪里可以使用Lambda表达式 + 什么是函数式接口

Java8学习笔记 - 在哪里可以使用Lambda表达式 + 什么是函数式接口