在編程語言中,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);