在编程语言中,lambda指的是一种运算符,用来表示:javascript
closures(闭包)
anonymous functions(匿名函数)
旧的语法经过传递数据来完成,而Lambda表达式更注重于行为。在java中,Lambda表达式是个对象,它们必须依附于一类特别的对象类型 —— 函数式接口(functional interface)java
传递行为,而不单单是值编程
(type1 arg1, type2 arg2, ..., typeN argN) -> { body }闭包
Lambda示例并发
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); list.forEach(new Consumer<Integer>(){ @Override public void accept(Integer integer){ System.out.println(integer); } });
@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); }; } }
@FunctionalInterface
:凡是接口上带上这个注解,即为函数式接口。一个函数式接口有且只有一个精确的抽象方法。函数式接口的实例能够经过Lamdba表达式的方法引用
或构造方法引用
来去建立。
若是使用了@FunctionalInterface
注解,但却不知足如下两个条件,编译器会返回编译异常:app
Interface接口类型
若是这个抽象方法复写了或者重写了Object
类里的public方法
,那么这个接口也不属于函数式接口。由于java的任意实现,必定都会有一个来自java.lang.Object的实现。编程语言
@FunctionalInterface public interface MyInterface{ // 抽象方法 void test(); // Object的抽象方法 String toString(); }
toString()
是Object的抽象方法,因此编译器认为MyInterface依然是一个函数式接口。ide
public class UsingFunctionInterface{ public void myUsingInterface(MyInterface interface){ System.out.println("start"); interface.test(); System.out.println("end"); } public static void main(String[] args){ UsingFunctionInterface u = new UsingFunctionInterface(); // 写法1 u.myUsingInterface(() -> { System.out.println("test print"); }); //写法2 MyInterface myInterface = () -> { System.out.println("test print"); }; u.myUsingInterface(myInterface); } }
Lamdba表达式弱化了抽象方法名的意义,将方法实现(即->
右边的部分,直接做为函数式接口的实现)。函数
关于函数式接口的特色ui
@FunctionalInterface
,那么编译器就会按照函数式接口的定义来要求该接口@FunctionalInterface
,那么编译器依旧会将该接口看做是函数式接口。Lamdba表达式
、方法的实例
、构造方法的实例
来建立。上述的总结:@FunctionalInterface
只是帮助编译器检查函数式接口是否合法。
若是一个接口须要用做函数式接口,那么最好加上
@FunctionalInterface
。
list.foreach()
default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } }
foreach最特殊的就是用了default关键字
。jdk1.8后,接口里也能够有方法实现。一旦在接口中使用了default关键字作方法的实现,称之为默认方法(default Method)
。
默认方法的出现一方面保证了jdk8的新特性的加入,另外一方面保证了jdk8能够向上兼容。
Consumer
是一个操做,这个操做接受一个单个的输入元素,而且不返回结果。与其余函数式接口不一样的是,它可能会修改接收到的操做。
Consumer的语义是消费者
,因此顾名思义,这个类中的抽象方法void accept(T t)
的语义为:接受一个类,并将其消费掉,并不关心结果
外部迭代
:指的是利用外部的迭代器,对一个集合进行迭代。内部迭代
:经过集合自己,利用Lambda表达式进行迭代。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6); // 外部迭代 for(Integer i : numbers){ System.out.println(i); } // 内部迭代 : 函数式接口 numbers.forEach(new Consumer<Integer>{ public void accept(){ System.out.println(i); } }); // 内部迭代: 函数式接口 + Lambda表达式 numbers.forEach(i -> System.out.println(i) ) // 内部迭代: 方法引用 numbers.forEach(System.out::println)
default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); }
stream是在Collection接口的默认方法,返回一个串行流。
当spliterator(分割迭代器)
方法不能返回一个分割迭代器时(不可变的、延迟的、并发的分割迭代器),stream方法须要被重写。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
,map自己的含义是映射,将一个给定的元素映射为另外一个元素并返回一个新的Stream。
List<String> list = Arrays.asList("a", "b", "c"); List<String> list2 = new ArrayList<>(); // Lamdba表达式实现 list.stream().map(i -> i.toUpperCase()).forEach(i -> list2.add(i)); // 方法引用实现 list.stream().map(String::toUpperCase).forEach(list2::add);