爲何要用函數式編程。看例子:java
final List<BigDecimal> prices = Arrays.asList( new BigDecimal("10"), new BigDecimal("30"), new BigDecimal("17"), new BigDecimal("20"), new BigDecimal("15"), new BigDecimal("18"), new BigDecimal("45"), new BigDecimal("12")); BigDecimal totalOfDiscountedPrices = BigDecimal.ZERO; for(BigDecimal price : prices) { if(price.compareTo(BigDecimal.valueOf(20)) > 0) totalOfDiscountedPrices = totalOfDiscountedPrices.add(price.multiply(BigDecimal.valueOf(0.9))); } System.out.println("Total of discounted prices: " + totalOfDiscountedPrices);
更優雅的方式,是使用聲明式的代碼:編程
final BigDecimal totalOfDiscountedPrices = prices.stream() .filter(price -> price.compareTo(BigDecimal.valueOf(20)) > 0) .map(price -> price.multiply(BigDecimal.valueOf(0.9))) .reduce(BigDecimal.ZERO, BigDecimal::add); System.out.println("Total of discounted prices: " + totalOfDiscountedPrices);
使用函數式代碼的好處:數組
函數式接口就是僅聲明瞭一個方法的接口,好比咱們熟悉的Runnable,Callable,Comparable等均可以做爲函數式接口。固然,在Java 8中,新添加了一類函數式接口,如Function,Predicate,Consumer,Supplier等。ide
@FunctionalInterface public static interface IntHandler{ void handle(int i); }
在函數式接口中,能夠聲明0個或者多個default方法,這些方法在接口內就已經被實現了。所以,接口的default方法也是Java 8中引入的一個新概念。函數式編程
default method 默認方法,又稱做虛擬擴展方法或保衛者方法,例子:函數
import java.util.Objects; import java.util.function.Consumer; public interface Iterable<T> { default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } } }
爲何須要default method?ui
函數式接口使用 @FunctionalInterface
註解進行標註。雖然這個註解的使用不是強制性的,可是使用它的好處是讓此接口的目的更加明確,同時編譯器也會對代碼進行檢查,來確保被該註解標註的接口的使用沒有語法錯誤。this
lambda表達式即匿名函數,它是一段沒有函數名的函數體,能夠做爲參數直接傳遞給相關調用者。lua
Lambda 表達式一般使用 (argument) -> (body) 語法書寫,例如:線程
(arg1, arg2...) -> { body } (type1 arg1, type2 arg2...) -> { body }
舉例來講:
(int a, int b) -> { return a + b; } () -> System.out.println("Hello World"); (String s) -> { System.out.println(s); } () -> 42 () -> { return 3.1415 };
Lambda 表達式的結構
每一個 Lambda 表達式都能隱式地賦值給函數式接口,例如,咱們能夠經過 Lambda 表達式建立 Runnable 接口的引用。
Runnable r = () -> System.out.println("hello world");
方法引用是Java 8中提出的用來簡化lambda表達式的一種手段。它經過類名和方法名來定位到一個靜態方法或者實例方法。 方法引用在Java 8中的使用很是靈活。總的來講,能夠分爲如下幾種:
靜態方法引用:ClassName::methodName
List<Integer> ints = Arrays.asList(1, 2, 3); ints.sort(Integer::compare);
實例上的實例方法引用 instanceReference::methodName,方法引用使用」::」定義,」::」的前半部分表示類名或者實例名,後半部分表示方法名稱。若是是構造函數,則使用new表示。
words.forEach(System.out::println); words.stream().map(word -> word.length()); // lambda words.stream().map(String::length); // method reference // lambda words.stream().map(word -> { return new StringBuilder(word); }); // constructor reference words.stream().map(StringBuilder::new);
Lambda 表達式舉例
1.線程初始化
//舊方法: new Thread(new Runnable() { @Override public void run() { System.out.println("Hello from thread"); } }).start(); //新方法: new Thread( () -> System.out.println("Hello from thread") ).start();
2.打印數組全部元素,在下面的例子中,咱們先是用經常使用的箭頭語法建立 Lambda 表達式,以後,使用 Java 8 全新的雙冒號(::)操做符將一個常規方法轉化爲 Lambda 表達式:
//Old way: List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); for(Integer n: list) { System.out.println(n); } //New way: List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); list.forEach(n -> System.out.println(n)); //or we can use :: double colon operator in Java 8 list.forEach(System.out::println);
3.斷言函數式接口使用
import java.util.Arrays; import java.util.List; import java.util.function.Predicate; public class Test { public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7); System.out.println("Print all numbers:"); evaluate(list, n -> true); System.out.println("Print no numbers:"); evaluate(list, n -> false); System.out.println("Print even numbers:"); evaluate(list, n -> n % 2 == 0); System.out.println("Print odd numbers:"); evaluate(list, n -> n % 2 == 1); System.out.println("Print numbers greater than 5:"); evaluate(list, n -> n > 5); } public static void evaluate(List<Integer> list, Predicate<Integer> predicate) { for (Integer n : list) { if (predicate.test(n)) System.out.print(n + " "); } } }
使用匿名類與 Lambda 表達式的一大區別在於關鍵詞的使用。對於匿名類,關鍵詞 this 解讀爲匿名類,而對於 Lambda 表達式,關鍵詞 this 解讀爲寫就 Lambda 的外部類。
Lambda 表達式與匿名類的另外一不一樣在於二者的編譯方法。Java 編譯器編譯 Lambda 表達式並將他們轉化爲類裏面的私有函數,它使用 Java 7 中新加的 invokedynamic 指令動態綁定該方法
雖然 Java 8 的接口的默認方法就像抽象類,能提供方法的實現,可是他們倆仍然是 不可相互代替的: