Lambda表達式能夠是一段能夠傳遞的代碼,它的核心思想是將面向對象中的傳遞數據變成傳遞行爲,也就是行爲參數化,將不一樣的行爲做爲參數傳入方法。java
隨着函數式編程思想的引進,Lambda表達式讓能夠用更加簡潔流暢的代碼來代替以前冗餘的Java代碼。express
口說無憑,直接上個例子吧。在Java8以前,關於線程代碼是這樣的:編程
class Task implements Runnable{ @Override public void run() { System.out.println("Java8 以前 實現Runnable接口中的run方法"); } } Runnable t = new Task();
咱們定義了一個Task類,讓它實現Runnable接口,實現僅有的run方法,咱們但願執行的線程體雖然只有一句話,但咱們仍然花了大量大代碼去定義。爲了簡化,咱們能夠採用匿名內部類的方式:數組
Runnable taskBeforeJava8 = new Runnable() { @Override public void run() { System.out.println("Java8 以前的寫法, 傳入匿名類"); } };
可是,其實仍是不夠簡潔,咱們用Lambda的寫法是這樣的:app
// java8 以後 Runnable taskAfterJava8 = () -> System.out.println("Java8 以後的寫法,lambda表達式");
咱們僅僅使用()
和->
就完成了這件事,是否是很是簡潔呢?若是你以爲雖然Lambda寫法簡潔,可是它的規則讓人摸不着頭腦,那就跟着我接下去學叭。ide
(parameters) -> action (parameters) -> expression (parameters) -> {statements;}
parameters表明變量,能夠爲空,能夠爲單,能夠爲空,你能想到的方式他均可以。函數式編程
action是實現的代碼邏輯部分,能夠是一行代碼expression
,也能夠是一個代碼片斷statements
。若是是代碼片斷,須要加上{}
。函數
下面是一些合法的示例,你能夠看看有沒有掌握:線程
表達式 | 描述 |
---|---|
() -> 1024 |
不須要參數,返回值爲1024 |
x -> 2 * x |
接收參數x,返回其兩倍 |
(x, y) -> x - y |
接收兩個參數,返回它們的差 |
(int x, int y) -> x + y |
接收兩個int類型參數,返回他們的和 |
(String s) -> print(s) |
接收一個String對象,並打印 |
什麼是函數式接口?函數式接口是隻有一個抽象方法的接口,用做lambda表達式的類型。code
@FunctionalInterface // 此註解做用的接口 只能擁有一個抽象方法 public interface Runnable { public abstract void run(); }
在這裏,@FunctionalInterface
註解是非必須的,有點相似於@Override
,起強調做用,若是你的接口標註該註解,卻沒有遵循它的原則,編譯器會提示你修改。
JDK原生爲咱們提供了一些經常使用的函數式編程接口,讓咱們在使用他們編程時,沒必要關心接口名,方法名,參數名,只需關注它的參數類型,參數個數,返回值。
接口 | 參數 | 返回值 | 類別 | 示例 |
---|---|---|---|---|
Consumer | T | void | 消費型接口 | 打印輸出某個值 |
Supplier | None | T | 供給型接口 | 工廠方法獲取一個對象 |
Function | T | R | 函數型接口 | 獲取傳入列表的總和 |
Predicate | T | boolean | 斷言型接口 | 判斷是否以summer爲前綴 |
/** * 消費型接口, 傳入T 無返回值 */ public static void consumerTest() { Consumer<Integer> consumer = num -> System.out.println(num * num); consumer.accept(10); }
/** * 供給型接口, 無參數,返回T */ public static void supplierTest() { Supplier<Object> supplier = () -> new Object(); System.out.println(supplier.get()); }
/** * 斷言型 傳入參數T ,返回boolean */ public static void predicateTest() { Predicate<String> predicate = name -> name.startsWith("summer"); System.out.println(predicate.test("summerday")); }
/** * 函數型接口 傳入T 返回R */ public static void functionTest() { List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); Function<List<Integer>, Integer> function = num -> { int res = 0; for (int n : list) { res += n; } return res; }; Integer num = function.apply(list); System.out.println(num); }
方法引用能夠看做特定Lambda表達式的快捷寫法,主要分爲如下兩種:
/** * 方法引用 * 1. 指向靜態方法的方法引用 * 2. 指向現有對象的實例方法的方法引用 * * @author Summerday */ public class MethodReferenceTest { public static List<String> getList(List<String> params, Predicate<String> filter) { List<String> res = new LinkedList<>(); for (String param : params) { if (filter.test(param)) { res.add(param); } } return res; } // 靜態方法 public static boolean isStartWith(String name) { return name.startsWith("sum"); } public static void main(String[] args) { List<String> params = Arrays.asList("summerday","tqbx","天喬巴夏","summer",""); //靜態方法的方法引用 getList(params, name -> MethodReferenceTest.isStartWith(name)); List<String> list = getList(params, MethodReferenceTest::isStartWith); System.out.println(list); // 指向現有對象的實例方法的方法引用 getList(params, name -> name.isEmpty()); List<String> sum = getList(params, String::isEmpty); System.out.println(sum); } }
/** * 數組引用 * @author Summerday */ public class ArrayReferenceTest { public static void main(String[] args) { // 普通lambda Function<Integer,String[]> fun1 = x -> new String[x]; String[] res1 = fun1.apply(10); System.out.println(res1.length); // 數組引用寫法 Function<Integer,String[]> fun2 = String[]::new; String[] res2 = fun2.apply(10); System.out.println(res2.length); } }
/** * 構造器引用 * @author Summerday */ public class ConstructorReferenceTest { public static void main(String[] args) { // 普通lambda Supplier<User> sup = () -> new User(); // 構造器引用 Supplier<User> supplier = User::new; User user = supplier.get(); System.out.println(user); } } class User{ }
Predicate,Function,Supplier,Consumer,BinaryOperator
。Classname::method
。