《Java in Action》-1 第3章 Lambda表达式
2016-10-02 23:45
405 查看
第3章 Lambda表达式
3.1 Lambda管中窥豹
Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但他们有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。Lamdba表达式有三个部分:
参数列表 + 箭头 + Lambda主体
基本语法:
(parameters) -> expression
或
(parameters) -> {statements; }
3.2 在哪里以及如何使用Lambda
3.2.1 函数式接口函数式接口就是只定义一个抽象方法的接口。(注意只有一个)
Lambda表示式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实现。
因为Runnable是一个只定义了一个抽象方法run的函数式接口,所以可以以下这样:
Runnable a = () -> System.out.println("hi 1");
a.run();
3.2.2 函数描述符
函数式接口的的抽象方法的签名基本上就是Lambda表达式的签名。将这种抽象方法叫作函数描述符。
3.3 把Lambda付诸实践:环绕执行模式
3.4使用函数式接口
函数式接口定义且只定义了一个抽象方法。函数式接口很有用,因为抽象方法的签名可以描述Lambda表达式的签名。函数式接口的抽象方法的签名称为函数描述符。Java api中已经有了几个函数式接口,包括Comparable、Runnable和Callable。
Java 8引入了几个新的函数式接口,包括Predicate、Comsumer和Function。
3.4.1 Predicate
java.util.function.Predicate<T>接口定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean。
public class PredicateTest { private <T> List<T> choiceList(List<T> lists,Predicate<T> pres) { ArrayList<T> arrayList = new ArrayList<>(); for(T t : lists) { if(pres.test(t)) { arrayList.add(t); } } return arrayList; } public static void main(String[] args) { PredicateTest predicateTest = new PredicateTest(); List<Integer> choiceList = predicateTest.choiceList(Arrays.asList(1,2,3,4,5), (Integer i) -> { if(i > 3) {return true;}else{return false;}}); System.out.println(choiceList); } }
3.4.2 Consumer
java.util.function.Consumer<T>定义了一个名叫accept的抽象方法,它接受泛型T的对象,没有返回(void)。
public class ConsumerTest { private <T> void foreachList(List<T> lists,Consumer<T> consm) { for(T t : lists){ consm.accept(t); } } public static void main(String[] args) { ConsumerTest consumerTest = new ConsumerTest(); consumerTest.foreachList(Arrays.asList("Consumer Interface 1","Consumer 2","Consumer 3"), (String s) -> System.out.println(s)); } }
3.4.3 Function
java.util.function.Function<T,R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。
public class FunctionTest { private <T,R> List<R> functionLists(List<T> lists , Function<T, R> funs){ ArrayList<R> arrayList = new ArrayList<>(); for(T t : lists) { R apply = funs.apply(t); arrayList.add(apply); } return arrayList; } public static void main(String[] args) { FunctionTest functionTest = new FunctionTest(); List<Integer> functionLists = functionTest.functionLists(Arrays.asList("a","bc","edf"), (String s) -> s.length()); System.out.println(functionLists); } }
3.5 类型检查、类型推断以及限制
3.5.1 类型检查Lambda的类型是从使用Lambda的上下文推断出来的。
3.5.2 同样的Lambda,不同的函数式接口
*特殊的void兼容规则
如果一个Lambda的主体是一个语句表达式,它就和一个返回void的函数描述符兼容。
3.5.3 类型推断
Java编译器会从上下文(目标类型)推断出用什么函数接口来配合Lambda表达式,这意味着他也可以推断适合Lambda的签名,因为函数描述符可以通过目标类型来得到。这样,编译器可以了解Lambda表达式的参数类型,可以在Lambda语法中省去标注参数类型。
可以这样:
List<Apple> greenApples =
filter(inventory, a -> "green".equals(a.getColor()));
Comparator<Apple> c = (a1,a2) -> a1.getWeight().compareTo(a2.getWeight());
3.5.4 使用局部变量
Lambda表达式也允许使用自由变量,就像匿名类一样,他们被称作捕获Lambda。
Lambda可以没有限制地捕获实例变量和静态变量,但局部变量必须显示声明为final或事实上是final。
int portNumber = 1337;
Runnable r = () -> System.out.println(portNumber);
*闭包
闭包就是一个函数的实例,且它可以无限制地访问那个函数的非本地变量。
3.6 方法引用
方法引用让你可以重复使用现有的方法定义,并像Lambda一样传递他们。先前:
inventory.sort((Apple a1,Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
之后:
inventory.sort(comparing(Apple::getWeight));
Apple :: getWeight就是引用Apple类中定义的方法getWeight。
3.6.1 管中窥豹
*如何构建方法引用
(1)指向静态方法的方法引用
(例如Integer的parseInt方法,写作Integer :: parseInt)。
(2)指向任意类型实例方法的方法引用
(例如String的length方法,写作String :: length)
(3)指向现有对象的实例方法的方法引用
()
例子:
List<String> str = Arrays.asList("a","b","A","b");
str.sort((s1,s2) -> s1.compareToIgnoreCase(s2));
变成:
str.sort(String :: compareToIgnoreCase);
3.6.2 构造函数引用
对于一个现有构造函数,你可以利用它的名称和关键字new来创建它的一个引用:ClassName :: new 。
Supplier<Apple> c1 = () -> new Apple();
Apple a1 = c1.get();
等价于:
Supplier<Apple> c1 = Apple ::
new;
Apple a1 = c1.get();
3.7 Lambda和方法引用实战
想要实现的最终解决方案是这样的:3.8 复合Lambda表达式的有用方法
3.8.1 比较器复合Comparator<Apple> c = Comparator.comparing(Apple
:: getWeight);
1.逆序
inventory.sort(comparing(Apple
:: getWeight).reversed());//按重量递减排序。
2.比较器链
inventory.sort(comparing(Apple :: getWeight)
.reversed()
.thenComparing(Apple :: getCountry)
);
3.8.2 谓词复合
谓词接口包括三个方法:negate、and和or,让你可以重用已有的Predicate来创建更复杂的谓词。
使用negate方法来返回一个Predicate的非:
Predicate<Apple> notRedApple = redApple.negate();
3.8.3 函数复合
可以把Function接口所代表的Lambda表达式复合起来,Function接口为此配置了andThen和compose两个默认方法,它们都会返回Function的一个实例。
Function<Integer,Integer> f = x -> x + 1;
Function<Integer,Integer> g = x -> x * 2;
Function<Integer,Integer> h = f.andThen(g);//数学上写作g(f(x))
int result = h.apply(1);//返回4
Function<Integer,Integer> f = x -> x + 1;
Function<Integer,Integer> g = x -> x * 2;
Function<Integer,Integer> h = f.compose(g);//数学上写作f(g(x))
int result = h.apply(1);//返回4
3.9 数学中的类似思想
3.10 小结
*Lambda表达式可以理解为一种匿名函数:它没有名称,但有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。*Lambda表达式让你可以简洁地传递代码
*函数式接口就是仅仅声明了一个抽象方法的接口
*只有在接受函数式接口的地方才可以使用Lambda表达式
*Lambda表达式允许你直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例。
*Java 8 自带一些常用的函数式接口,放在java.util.function包里,包括Predicate<T>,Function<T,R>,Supplier<T>,BinaryOperator<T>。
*为了避免装箱操作,对通用函数式接口的原始类型特化:IntPredicate,IntToLongFunction等。
*Lambda表达式所需要代表的类型称为目标类型。
*方法引用让你重复使用现有的方法实现并直接传递它们。
*Comparator、Predicate和Function等函数式接口都有几个可以用来结合Lambda表达式的默认方法。
相关文章推荐
- Lambda 表达式 in java 8
- Java in Action - Struts分页显示
- Struts in Action: Building Web Applications with the Leading Java Framework
- Java 8为什么需要Lambda表达式
- c# in deep 之Lambda表达式
- Java 8 的 lambda 表达式
- 《Java in Action 》
- 在 Eclipse IDE 中试用 Lambda 表达式 Java
- Java 8为什么需要Lambda表达式
- Java 8 与Lambda表达式
- Quick to Redis,Java Client:Jedis in Action
- JavaServer Faces in Action
- 为LINQ服务的C#新特性总结篇---扩展方法,匿名委托,lambda表达式,Action委托,Func委托,Linq中的order by,top和sum函数
- JavaServer Faces JSF in Action
- Thinking in Java 第四版本--第3章练习题答案--for,break,switch,case,default
- thinking in java 第3章 控制程序流程
- 使用Action、Func和Lambda表达式
- 浅谈Java 7的闭包与Lambda表达式之优劣
- 说说Java 8中的Lambda表达式
- Java SE8函数式接口与Lambda表达式