Bootstrap

ForEach与lamda表达式

1.1 lamada表达式

  • lambda表达式仅能放入如下代码: 预定义使用了 @Functional 注释的函数式接口,自带一个抽象函数的方法,或者SAM(Single Abstract Method 单个抽象方法)类型。这些称为lambda表达式的目标类型,可以用作返回类型,或lambda目标代码的参数。例如,若一个方法接收Runnable、Comparable或者 Callable 接口,都有单个抽象方法,可以传入lambda表达式。类似的,如果一个方法接受声明于 java.util.function 包内的接口,例如 Predicate、Function、Consumer 或 Supplier,那么可以向其传lambda表达式。

  • lambda表达式内可以使用方法引用,仅当该方法不修改lambda表达式提供的参数。本例中的lambda表达式可以换为方法引用,因为这仅是一个参数相同的简单方法调用。

list.forEach(n -> System.out.println(n)); 
list.forEach(System.out::println);  // 使用方法引用

然而,若对参数有任何修改,则不能使用方法引用,而需键入完整地lambda表达式,如下所示:

list.forEach((String s) -> System.out.println("*" + s + "*"));

事实上,可以省略这里的lambda参数的类型声明,编译器可以从列表的类属性推测出来。

  • lambda内部可以使用静态非静态局部变量,这称为lambda内的变量捕获。
  • Lambda表达式在Java中又称为闭包匿名函数,所以如果有同事把它叫闭包的时候,不用惊讶。
  • Lambda方法在编译器内部被翻译成私有方法,并派发 invokedynamic 字节码指令来进行调用。可以使用JDK中的 javap 工具来反编译class文件。使用 javap -p 或 javap -c -v 命令来看一看lambda表达式生成的字节码。大致应该长这样:
private static java.lang.Object lambda$0(java.lang.String);
  • lambda表达式有个限制,那就是只能引用 final 或 final 局部变量,这就是说不能在lambda内部修改定义在域外的变量。
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” 另外,只是访问它而不作修改是可以的,如下所示:

List<Integer> primes =Arrays.asList(newInteger[]{2,3,5,7});
int factor =2; 
primes.forEach(element ->{System.out.println(factor*element);});

1.2.lamada语法

  • (参数) -> 表达式 或 (参数) -> {方法体;}

1.形参列表:

形参列表允许省略形参类型,若形参列表中只有一个参数,形参列表的圆括号也可以省略代码

2.箭头(->)

必须通过英文中划线号和大于符号组成

3.代码块:

如果代码块只包含一条语句,lambda表达式允许省略代码块的花括号,那么这条语句就不要用花括号表示语句结束

lambda代码块只有一条return语句,甚至可以省略return关键字

lambda表达式需要返回值,而它的代码块中仅有一条省略了return的语句,lambda表达式会自动返回这条语句的结果

Lambda语法注意点:

1,参数类型可以省略

2,假如只有一个参数,()括号可以省略

3,如果方法体只有一条语句,{}大括号可以省略

4,如果方法体中唯一的语句是return返回语句,那省略大括号的同事return也要省略

1.3 常用例子

#匿名类简写

new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();

// 用法
(params) -> expression
(params) -> statement
(params) -> { statements }

方法引用

构造引用

// Supplier<Student> s = () -> new Student();
Supplier<Student> s = Student::new;

对象::实例方法 Lambda表达式的(形参列表)与实例方法的(实参列表)类型,个数是对应

// set.forEach(t -> System.out.println(t));
set.forEach(System.out::println);

类名::静态方法

// Stream<Double> stream = Stream.generate(() -> Math.random());
Stream<Double> stream = Stream.generate(Math::random);

类名::实例方法

//  TreeSet<String> set = new TreeSet<>((s1,s2) -> s1.compareTo(s2));
/*  这里如果使用第一句话,编译器会有提示: Can be replaced with Comparator.naturalOrder,这句话告诉我们
  String已经重写了compareTo()方法,在这里写是多此一举,这里为什么这么写,是因为为了体现下面
  这句编译器的提示: Lambda can be replaced with method reference。好了,下面的这句就是改写成方法引用之后: 
*/
TreeSet<String> set = new TreeSet<>(String::compareTo);

;