概念
Lambda 大写 Λ,小写 λ。读音:lan b(m) da(兰木达)['læmdə]。
Lambda 表达式Lambda expression :基于数学中的 λ 演算得名,直接对应于其中的 Lambda 抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数,在 Java 中又称为闭包或匿名函数。而 λ演算 是函数式编程的基础,所以 Lambda 表达式具有部分函数式语言的特征。特点就是简(省 )单(代 )优(码 )雅。
重要特性 Lambda 表达式的语法1 2 3 (params) -> expression (params) -> statement (params) -> { statements }
示例:
1 2 3 4 5 6 7 8 9 10 11 new Thread(new Runnable() { @Override public void run () { System.out.println("Before Java8, too much code for too little to do" ); } }).start(); new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!" ) ).start();
其他示例:
参数可以是零个或多个
参数类型可指定,可省略(根据表达式上下文推断)
参数包含在圆括号中,用逗号分隔
表达式主体可以是零条或多条语句,包含在花括号中
表达式主体只有一条语句时,花括号可省略
表达式主体有一条以上语句时,表达式的返回类型与代码块的返回类型一致
表达式只有一条语句时,表达式的返回类型与该语句的返回类型一致
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ()-> System.out.println("no argument" ); x->x+1 (x,y)->x+y View.OnClickListener oneArgument = view->Log.d(TAG,"one argument" ); View.OnClickListener oneArgument = (View view)->Log.d(TAG,"one argument" ); View.OnClickListener multiLine = (View view)->{ Log.d(TAG,"multi statements" ); Log.d(TAG,"second line" ); } (int x)->x+1
方法引用 Method Reference 格式:类名::方法名。注意:只需要写方法名,不需要写括号。方法引用来简写 Lambda 表达式中已经存在的方法。示例:
1 2 3 4 5 6 7 8 9 10 11 12 List features = Arrays.asList("Lambdas" , "Default Method" , "Stream API" ); for (String feature : features) { System.out.println(feature); } List features = Arrays.asList("Lambdas" , "Default Method" , "Stream API" ); features.forEach(n -> System.out.println(n)); features.forEach(System.out::println);
方法引用的四种形式:
引用静态方法ContainingClass::staticMethodName
引用某个类型的任意对象的实例方法ContainingType::methodName
引用构造方法ClassName::new
引用某个对象 的实例方法containingObject::instanceMethodName
函数式接口 FI 函数式接口:指仅含有一个抽象方法的接口。首先是一个接口,然后就是在这个接口里面只能有一个抽象方法。以 @Functionalnterface 标注,简称 FI 。但是加不加 @FunctionalInterface 对于接口是不是函数式接口没有影响,该注解只是提醒编译器去检查该接口是否仅包含一个抽象方法。
1 2 3 4 interface OnClickListener { void onClick (View v) ; }
引申Java 8 中引入了新特性,向接口中引入默认方法 和静态方法 ,以此来减少抽象类和接口之间的差异。也就是说新特性中,接口可以和抽象类一样,具有默认或静态的方法实现,如示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 文件:java.util.function.Predicate.java @FunctionalInterface public interface Predicate <T > { boolean test (T 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 8 的这个新特性就是主要解决这类问题的,放到接口中后,实现类不需要重写,确保老版本的代码能够兼容。
Lambda 实现函数式接口Lambda 通过该方式,大大简化了代码量,也是最基本的表达式之一。
1 2 3 4 5 6 7 8 9 10 button.setOnClickListener(new View.onOnClickListener(){ @Override public void onClick (View v) { v.setText("abc" ); } }); button.setOnClickListener(v -> v.setText("abc" ));
this 关键字Lambda 表达式即将正式取代 Java 代码中的匿名内部类,他们在关键字 this 上有很大不同:匿名类的 this 关键字指向匿名类,而 Lambda 表达式的 this 关键字指向包围 Lambda 表达式的类。
局部变量 Lambda 表达式对局部变量有个限制,那就是只能引用 final 局部变量,这就是说不能在 Lambda 表达式内部修改定义在域外的变量。示例:
1 2 3 4 5 6 7 8 9 10 List<Integer> primes = Arrays.asList(new Integer[]{2 , 3 ,5 ,7 }); int factor = 2 ;primes.forEach(element -> { factor++; }); Compile time error : "local variables referenced from a lambda expression must be final or effectively final" primes.forEach(element -> { System.out.println(factor*element); });
集合的流式操作 流式操作: JDK8 的 Stream 是一个受到函数式编程和多核时代影响而产生的东西。java.util.stream 包,实现了集合的流式操作,流式操作包括集合的过滤,排序,映射等功能。根据流的操作性,又可以分为串行流和并行流。根据操作返回的结果不同,流式操作又分为中间操作和最终操作。大大方便了我们对于集合的操作:
最终操作:返回一特定类型的结果
中间操作:返回流本身
Map 和 Reduce 应用Map 和 Reduce 操作是函数式编程的核心操作,map 将集合类(例如列表)元素进行转换,也就是将参数转换成想要的返回值;reduce 函数可以将所有值合并成一个,又被称为折叠操作。示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 List costBeforeTax = Arrays.asList(100 , 200 , 300 , 400 , 500 ); double total = 0 ;for (Integer cost : costBeforeTax) { double price = cost + .12 *cost; total = total + price; } System.out.println("Total : " + total); List costBeforeTax = Arrays.asList(100 , 200 , 300 , 400 , 500 ); double bill = costBeforeTax.stream() .map((cost) -> cost + .12 *cost) .reduce((sum, cost) -> sum + cost) .get(); System.out.println("Total : " + bill);
过滤器 filter 过滤是在大规模集合上的一个常用操作,而使用 Lambda 表达式和流 API 过滤大规模数据集合非常简单。示例:
1 2 3 4 5 6 List<String> filtered = strList.stream() .filter(x -> x.length()> 2 ) .collect(Collectors.toList()); System.out.printf("Original List : %s, filtered list : %s %n" , strList, filtered);
参考文档