《java 8 实战》读书笔记 -第三章 Lambda表达式

第三章 Lambda表达式

函数式接口

函数式接口就是只定义一个抽象方法的接口,哪怕有不少默认方法,只要接口只定义了一个抽象方法,它就仍然是一个函数式接口。

经常使用函数式接口

图片描述
图片描述

函数描述符

函数式接口的抽象方法的签名称为函数描述符。

在哪里可使用Lambda?

只有在须要函数式接口的时候才能够传递Lambda
下哪些是使用Lambda表达式的有效方式?
(1)java

execute(() -> {});
public void execute(Runnable r){ 
r.run(); 
}

(2)app

return () -> "Tricky example ;-)";

(3)ide

Predicate<Apple> p = (Apple a) -> a.getWeight();

答案:只有1和2是有效的
第一个例子有效,是由于Lambda() -> {}具备签名() -> void,这和Runnable中的
抽象方法run的签名相匹配。请注意,此代码运行后什么都不会作,由于Lambda是空的!
第二个例子也是有效的。事实上,fetch方法的返回类型是Callable<String>。
Callable<String>基本上就定义了一个方法,签名是() -> String,其中T被String代替
了。由于Lambda() -> "Trickyexample;-)"的签名是() -> String,因此在这个上下文
中可使用Lambda。
第三个例子无效,由于Lambda表达式(Apple a) -> a.getWeight()的签名是(Apple) ->
Integer,这和Predicate<Apple>:(Apple) -> boolean中定义的test方法的签名不一样。函数

@FunctionalInterface又是怎么回事

这个标注用于表示该接口会设计成一个函数式接口,@FunctionalInterface不是必需的,它就像是@Override标注表示方法被重写了。

Java 7中的带资源的try语句

它已经简化了代码,由于你不须要显式地关闭资源了.fetch

public static String processFile() throws IOException { 
  try (BufferedReader br = 
  new BufferedReader(new FileReader("data.txt"))) { 
  return br.readLine(); 
  } 
}

函数式接口:Predicate断言

java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。

函数式接口:Consumer

java.util.function.Consumer<T>定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void)。

函数式接口:Function

java.util.function.Function<T, R>接口定义了一个叫做apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。
eg:ui

@FunctionalInterface 
public interface Function<T, R>{ 
 R apply(T t); 
} 
public static <T, R> List<R> map(List<T> list, 
 Function<T, R> f) { 
 List<R> result = new ArrayList<>(); 
 for(T s: list){ 
 result.add(f.apply(s)); 
 } 
 return result; 
} 
// [7, 2, 6] 
List<Integer> l = map( 
 Arrays.asList("lambdas","in","action"), 
 (String s) -> s.length() 
 );

避免自动装箱、拆箱

通常来讲,针对专门的输入参数类型的函数式接口的名称都要加上对应的原始类型前缀,好比DoublePredicate、IntConsumer、LongBinaryOperator、IntFunction等。Function接口还有针对输出参数类型的变种:ToIntFunction<T>、IntToDoubleFunction等。

关于异常

请注意,任何库中的函数式接口都不容许抛出受检异常(checked exception)。若是你须要Lambda表达式来抛出异常,有两种办法:定义一个本身的函数式接口,并声明受检异常,或者把Lambda包在一个try/catch块中。

目标类型

Lambda表达式须要的类型称为目标类型。(即对应的函数式接口)

类型推断

你还能够进一步简化你的代码。Java编译器会从上下文(目标类型)推断出用什么函数式接
口来配合Lambda表达式,这意味着它也能够推断出适合Lambda的签名,由于函数描述符能够通
过目标类型来获得。这样作的好处在于,编译器能够了解Lambda表达式的参数类型,这样就可
以在Lambda语法中省去标注参数类型。换句话说,Java编译器会像下面这样推断Lambda的参数
类型:spa

List<Apple> greenApples = 
filter(inventory, a -> "green".equals(a.getColor()));

方法引用

为三种不一样类型的Lambda表达式构建方法引用的办法:
图片描述
List<String> str = Arrays.asList("a","b","A","B"); 
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));

Lambda表达式的签名与Comparator的函数描述符兼容。利用前面所述的方法,这个例子可
以用方法引用改写成下面的样子:设计

List<String> str = Arrays.asList("a","b","A","B"); 
str.sort(String::compareToIgnoreCase);

构造函数引用

对于一个现有构造函数,你能够利用它的名称和关键字new来建立它的一个引用:
ClassName::new。

构造函数引用
要怎么样才能对具备三个参数的构造函数,好比Color(int, int, int),使用构造函数引用呢?
答案:你看,构造函数引用的语法是ClassName::new,那么在这个例子里面就是Color::new。可是你须要与构造函数引用的签名匹配的函数式接口。可是语言自己并无提供这样的函数式接口,你能够本身建立一个:code

public interface TriFunction<T, U, V, R>{ 
 R apply(T t, U u, V v); 
}

如今你能够像下面这样使用构造函数引用了:对象

TriFunction<Integer, Integer, Integer, Color> colorFactory = Color::new;

Comparator 类内部comparing实现

comparing 方法一
查看 Comparator 类内部实现,还有一个 comparing 方法,实现以下,

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
           Function<? super T, ? extends U> keyExtractor)
   {
       Objects.requireNonNull(keyExtractor);
       return (Comparator<T> & Serializable)
           (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
   }

其返回值是 (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2)); 一个lambda表达式,也就是一个Compator
eg:

Comparator<Apple> c = Comparator.comparing(Apple::getWeight);

comparing 方法二

public static <T, U> Comparator<T> comparing(
           Function<? super T, ? extends U> keyExtractor,
           Comparator<? super U> keyComparator)
   {
       Objects.requireNonNull(keyExtractor);
       Objects.requireNonNull(keyComparator);
       return (Comparator<T> & Serializable)
           (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
                                             keyExtractor.apply(c2));
   }

和comparing 方法一不一样的是 该方法多了一个参数 keyComparator ,keyComparator 是建立一个自定义的比较器。

Collections.sort(employees, Comparator.comparing(
               Employee::getName, (s1, s2) -> {
                   return s2.compareTo(s1);
               }));

比较器复合

逆序

inventory.sort(comparing(Apple::getWeight).reversed());

比较器链
thenComparing方法就是作这个用的

inventory.sort(comparing(Apple::getWeight) 
.reversed().thenComparing(Apple::getCountry));

谓词复合(断言复合)

谓词接口包括三个方法:negate、and和or,让你能够重用已有的Predicate来建立更复
杂的谓词。好比,你可使用negate方法来返回一个Predicate的非,好比苹果不是红的:

Predicate<Apple> notRedApple = redApple.negate();

你可能想要把两个Lambda用and方法组合起来,好比一个苹果既是红色又比较重:

Predicate<Apple> redAndHeavyApple = 
redApple.and(a -> a.getWeight() > 150);

你能够进一步组合谓词,表达要么是重(150克以上)的红苹果,要么是绿苹果:

Predicate<Apple> redAndHeavyAppleOrGreen = 
redApple.and(a -> a.getWeight() > 150) 
.or(a -> "green".equals(a.getColor()));

这一点为何很好呢?从简单Lambda表达式出发,你能够构建更复杂的表达式,但读起来仍然和问题的陈述差很少!请注意,and和or方法是按照在表达式链中的位置,从左向右肯定优先级的。所以,a.or(b).and(c)能够看做(a || b) && c。

函数复合

你还能够把Function接口所表明的Lambda表达式复合起来。Function接口为此配了andThen和compose两个默认方法,它们都会返回Function的一个实例。

  • andThen方法会返回一个函数,它先对输入应用一个给定函数,再对输出应用另外一个函数;用compose方法,先把给定的函数用做compose的参>数里面给的那个函数,而后再把函数自己用于结果。
相关文章
相关标签/搜索