3分鐘看完Java 8——史上最強Java 8新特性總結之第一篇 函數式編程基礎

目錄

· 行爲參數化java

· Lambda表達式express

    · 概況編程

    · 函數式接口數組

    · 類型推斷app

    · 使用外層變量ide

    · 方法引用函數式編程

    · 複合Lambda表達式函數


 

行爲參數化

1. 理解函數式編程要先理解行爲參數化。this

2. 行爲參數化:一個方法接受多個不一樣的行爲做爲參數,並在內部使用它們,完成不一樣行爲的能力。spa

3. 行爲參數化優勢:可以讓代碼更好地適應不斷變化的需求,減輕將來的工做量。

4. 實現方式

    a) Java 8之前:經過接口實現類或接口匿名類實現。

    b) Java 8及之後:經過Lambda表達式實現。

5. 舉例

    a) 經過接口實現類實現行爲參數化

        i. Apple.java(後續舉例將屢次使用到該類)

 1 public class Apple {
 2 
 3     private Integer weight;
 4 
 5     private String color;
 6 
 7     public Apple(Integer weight, String color) {
 8         this.weight = weight;
 9         this.color = color;
10     }
11 
12     public Integer getWeight() {
13         return weight;
14     }
15 
16     public void setWeight(Integer weight) {
17         this.weight = weight;
18     }
19 
20     public String getColor() {
21         return color;
22     }
23 
24     public void setColor(String color) {
25         this.color = color;
26     }
27 
28     @Override
29     public String toString() {
30         return "weight=" + weight + " color=" + color;
31     }
32 }

        ii. ApplePredicate.java

1 public interface ApplePredicate {
2 
3     boolean test(Apple apple);
4 
5 }

        iii. AppleHeavyWeightPredicate.java

1 public class AppleHeavyWeightPredicate implements ApplePredicate {
2 
3     public boolean test(Apple apple) {
4         return apple.getWeight() > 150;
5     }
6 
7 }

        iv. AppleGreenColorPredicate.java

1 public class AppleGreenColorPredicate implements ApplePredicate {
2 
3     public boolean test(Apple apple) {
4         return "green".equals(apple.getColor());
5     }
6 
7 }

        v. Test.java

 1 import java.util.ArrayList;
 2 import java.util.Arrays;
 3 import java.util.List;
 4 
 5 public class Test {
 6 
 7     public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
 8         List<Apple> result = new ArrayList<>();
 9         for (Apple apple : inventory) {
10             if (p.test(apple)) {
11                 result.add(apple);
12             }
13         }
14         return result;
15     }
16 
17     public static void printApples(List<Apple> inventory) {
18         for (Apple apple : inventory) {
19             System.out.println(apple);
20         }
21     }
22 
23     public static void main(String[] args) {
24         List<Apple> inventory = Arrays.asList(
25                 new Apple(100, "red"),
26                 new Apple(110, "red"),
27                 new Apple(190, "red"),
28                 new Apple(170, "red"),
29                 new Apple(100, "green"),
30                 new Apple(120, "green"),
31                 new Apple(160, "green"),
32                 new Apple(180, "green")
33         );
34         List<Apple> newInventory1 = filterApples(inventory, new AppleHeavyWeightPredicate());
35         printApples(newInventory1);
36         System.out.println("-----");
37         List<Apple> newInventory2 = filterApples(inventory, new AppleGreenColorPredicate());
38         printApples(newInventory2);
39     }
40 
41 }

    b) 經過接口匿名類實現行爲參數化

        i. ApplePredicate.java

1 public interface ApplePredicate {
2 
3     boolean test(Apple apple);
4 
5 }

        ii. Test.java

 1 import java.util.ArrayList;
 2 import java.util.Arrays;
 3 import java.util.List;
 4 
 5 public class Test {
 6 
 7     public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
 8         List<Apple> result = new ArrayList<>();
 9         for (Apple apple : inventory) {
10             if (p.test(apple)) {
11                 result.add(apple);
12             }
13         }
14         return result;
15     }
16 
17     public static void printApples(List<Apple> inventory) {
18         for (Apple apple : inventory) {
19             System.out.println(apple);
20         }
21     }
22 
23     public static void main(String[] args) {
24         List<Apple> inventory = Arrays.asList(
25                 new Apple(100, "red"),
26                 new Apple(110, "red"),
27                 new Apple(190, "red"),
28                 new Apple(170, "red"),
29                 new Apple(100, "green"),
30                 new Apple(120, "green"),
31                 new Apple(160, "green"),
32                 new Apple(180, "green")
33         );
34         List<Apple> newInventory1 = filterApples(inventory, new ApplePredicate() {
35             @Override
36             public boolean test(Apple apple) {
37                 return apple.getWeight() > 150;
38             }
39         });
40         printApples(newInventory1);
41         System.out.println("-----");
42         List<Apple> newInventory2 = filterApples(inventory, new ApplePredicate() {
43             @Override
44             public boolean test(Apple apple) {
45                 return "green".equals(apple.getColor());
46             }
47         });
48         printApples(newInventory2);
49     }
50 
51 }

Lambda表達式

概況

1. Lambda表達式:可把Lambda表達式看做只有一個方法的接口匿名類,即沒有聲明名稱的方法,也能夠做爲參數傳遞給另外一個方法。

2. Lambda表達式特色

    a) 匿名:不像普通的方法那樣有一個明確的名稱。

    b) 函數:不像方法那樣屬於某個特定的類。但和方法同樣,Lambda有參數列表、函數主體、返回類型,還可能有能夠拋出的異常列表。

    c) 傳遞:能夠做爲參數傳遞給方法或存儲在變量中。

    d) 簡潔:無需像匿名類那樣寫不少模板代碼。

3. Lambda表達式語法

    a) 語法格式1

(parameters) -> expression

    b) 語法格式2

(parameters) -> { statements; }

    c) 舉例

場景

Lambda表達式

布爾表達式

(List<String> list) -> list.isEmpty()

建立對象

() -> new Apple(10, "red")

消費一個對象

(Apple a) -> {

    System.out.println(a.getWeight());

}

從一個對象中選擇/抽取

(String s) -> s.length()

組合兩個值

(int a, int b) -> a * b

比較兩個對象

(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())

4.Lambda表達式使用條件:只能在函數式接口上使用。

5.函數式接口(Functional Interface):只定義一個抽象方法的接口(注意不包括默認方法)。

6.舉例:經過Lambda表達式實現行爲參數化

    a) ApplePredicate.java

1 public interface ApplePredicate {
2 
3     boolean test(Apple apple);
4 
5 }

    b) Test.java

 1 import java.util.ArrayList;
 2 import java.util.Arrays;
 3 import java.util.List;
 4 
 5 public class Test {
 6 
 7     public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
 8         List<Apple> result = new ArrayList<>();
 9         for (Apple apple : inventory) {
10             if (p.test(apple)) {
11                 result.add(apple);
12             }
13         }
14         return result;
15     }
16 
17     public static void printApples(List<Apple> inventory) {
18         for (Apple apple : inventory) {
19             System.out.println(apple);
20         }
21     }
22 
23     public static void main(String[] args) {
24         List<Apple> inventory = Arrays.asList(
25                 new Apple(100, "red"),
26                 new Apple(110, "red"),
27                 new Apple(190, "red"),
28                 new Apple(170, "red"),
29                 new Apple(100, "green"),
30                 new Apple(120, "green"),
31                 new Apple(160, "green"),
32                 new Apple(180, "green")
33         );
34         List<Apple> newInventory1 = filterApples(inventory, (Apple apple) -> apple.getWeight() > 150);
35         printApples(newInventory1);
36         System.out.println("-----");
37         List<Apple> newInventory2 = filterApples(inventory, (apple) -> "green".equals(apple.getColor()));
38         printApples(newInventory2);
39     }
40 
41 }

函數式接口

1. Java 8自帶的函數式接口都在java.util.function包下。

2. 異常:Java 8自帶函數式接口都不容許拋出Checked Exception。若是須要Lambda表達式來拋出異常,要麼定義一個本身的函數式接口,並聲明Checked Exception,要麼把Lambda包在一個try/catch塊中。

3. 裝箱操做(Boxing):爲了不裝箱操做帶來的開銷問題,不該使用Predicate<T>或Function<T, R>等通用函數式接口,而應使用IntPredicate、IntToLongFunction等原始類型特化接口。

4. Java 8經常使用函數式接口

函數式接口

函數描述符

原始類型特化

Predicate<T>

T->boolean

IntPredicate

LongPredicate

DoublePredicate

Consumer<T>

T->void

IntConsumer

LongConsumer

DoubleConsumer

Function<T,R>

T->R

IntFunction<R>

IntToDoubleFunction

IntToLongFunction

LongFunction<R>

LongToDoubleFunction

LongToIntFunction

DoubleFunction<R>

ToIntFunction<T>

ToDoubleFunction<T>

ToLongFunction<T>

Supplier<T>

()->T

BooleanSupplier

IntSupplier

LongSupplier

DoubleSupplier

UnaryOperator<T>

T->T

IntUnaryOperator

LongUnaryOperator

DoubleUnaryOperator

BinaryOperator<T>

(T,T)->T

IntBinaryOperator

LongBinaryOperator

DoubleBinaryOperator

BiPredicate<L, R>

(L, R) -> boolean

 

BiConsumer<T, U>

(T, U) -> void

ObjIntConsumer<T>

ObjLongConsumer<T>

ObjDoubleConsumer<T>

BiFunction<T, U, R>

(T, U) -> R

ToIntBiFunction<T, U>

ToLongBiFunction<T, U>

ToDoubleBiFunction<T, U>

5. 舉例

    a) Lambda表達式與函數式接口對應

場景

Lambda表達式

對應的函數式接口

布爾表達式

(List<String> list) -> list.isEmpty()

Predicate<List<String>>

建立對象

() -> new Apple(10, "red")

Supplier<Apple>

消費一個對象

(Apple a) -> {

    System.out.println(a.getWeight());

}

Consumer<Apple>

從一個對象中選擇/抽取

(String s) -> s.length()

Function<String, Integer>

ToIntFunction<String>

組合兩個值

(int a, int b) -> a * b

IntBinaryOperator

比較兩個值

(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())

Comparator<Apple>

BiFunction<Apple, Apple, Integer>

ToIntBiFunction<Apple, Apple>

    b) Predicate<T>

 1 import java.util.ArrayList;
 2 import java.util.Arrays;
 3 import java.util.List;
 4 import java.util.function.Predicate;
 5 
 6 public class Test {
 7 
 8     public static <T> List<T> filter(List<T> list, Predicate<T> p) {
 9         List<T> results = new ArrayList<>();
10         for (T s : list) {
11             if (p.test(s)) {
12                 results.add(s);
13             }
14         }
15         return results;
16     }
17 
18     public static void main(String[] args) {
19         List<String> listOfStrings = Arrays.asList("", "A", "B", "", "C");
20         Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
21         List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);
22         for (String string : nonEmpty) {
23             System.out.println(string);
24         }
25     }
26 
27 }

    c) Consumer<T>

 1 import java.util.Arrays;
 2 import java.util.List;
 3 import java.util.function.Consumer;
 4 
 5 public class Test {
 6 
 7     public static <T> void forEach(List<T> list, Consumer<T> c){
 8         for(T i: list){
 9             c.accept(i);
10         }
11     }
12 
13     public static void main(String[] args) {
14         List<Integer> listOfNumbers = Arrays.asList(1, 2, 3, 4, 5);
15         forEach(listOfNumbers, (Integer i) -> System.out.println(i));
16     }
17 
18 }

    d) Function<T, R>

 1 import java.util.ArrayList;
 2 import java.util.Arrays;
 3 import java.util.List;
 4 import java.util.function.Function;
 5 
 6 public class Test {
 7 
 8     public static <T, R> List<R> map(List<T> list, Function<T, R> f) {
 9         List<R> result = new ArrayList<>();
10         for(T s: list){
11             result.add(f.apply(s));
12         }
13         return result;
14     }
15 
16     public static void main(String[] args) {
17         List<String> listOfStrings = Arrays.asList("lambdas", "in", "action");
18         List<Integer> l = map(listOfStrings, (String s) -> s.length());
19         for (Integer i : l) {
20             System.out.println(i);
21         }
22     }
23 
24 }

類型推斷

1. 類型推斷:同一個Lambda表達式就可賦予不一樣的函數式接口,只要它們的抽象方法簽名可以兼容。

1 Callable<Integer> c = () -> 42;
2 PrivilegedAction<Integer> p = () -> 42;
1 Comparator<Apple> c1 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
2 ToIntBiFunction<Apple, Apple> c2 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
3 BiFunction<Apple, Apple, Integer> c3 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

2. void兼容規則:若是一個Lambda的主體是一個語句表達式, 它就和一個返回void的函數描述符兼容(固然須要參數列表也兼容)。例如,雖然List.add方法返回boolean,但Consumer<T>的T->void仍然兼容。

Consumer<String> b = s -> list.add(s);

3. Object類:Object不是函數式接口。下面的代碼沒法編譯。

Object o = () -> {System.out.println("Tricky example"); };

4. Lambda表達式類型省略

    a) 參數類型省略:省略和不省略均可能更易讀。

1 // 沒有類型推斷
2 Comparator<Apple> c = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
3 // 有類型推斷
4 Comparator<Apple> c = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight());

    b) 參數括號省略:當Lambda僅有一個類型須要推斷的參數時,參數名稱兩邊的括號也能夠省略。

List<Apple> greenApples = filter(inventory, a -> "green".equals(a.getColor()));

使用外層變量

1. 外層變量限制:Lambda表達式只能使用顯式聲明爲final或其實是final的外層變量。與匿名類相似,但匿名類更嚴格(只能使用顯式聲明爲final的外層變量)。

2. 限制緣由

    a) Lambda表達式在訪問外層變量時,其實是在訪問它的副本,而不是訪問原始變量。

    b) 函數式編程不鼓勵使用外層變量(更容易並行)。

3. 舉例

    a) 可正常運行

1 int number = 100;
2 Runnable r = () -> System.out.println(number);
3 new Thread(r).start();

    b) 運行報錯「local variables referenced from a lambda expression must be final or effectively final」

1 int number = 100;
2 Runnable r = () -> System.out.println(number);
3 new Thread(r).start();
4 number = 200;

方法引用

1. 方法引用:能夠重複使用現有的方法定義,並像Lambda同樣傳遞。

2. 方法引用優勢:有時比Lambda表達式可讀性更好。

3. 方法引用的種類

    a) 指向靜態方法的方法引用,例如Integer.parseInt()方法,寫做Integer::parseInt。

    b) 指向任意類型實例方法的方法引用,例如String.length()方法,寫做String::length。

    c) 指向現有對象的實例方法的方法引用,例若有一個局部變量apple有getWeight()實例方法,apple::getWeight。

    d) 指向構造函數的方法引用,例如Date的構造方法,寫做Date::new。

    e) 針對構造函數、數組構造函數和父類調用(super-call)的一些特殊形式的方法引用。

4. 舉例

    a) Lambda表達式與方法引用對應

Lambda表達式

對應的方法引用

(Apple a) -> a.getWeight()

Apple::getWeight

() -> Thread.currentThread().dumpStack()

Thread.currentThread()::dumpStack

(str, i) -> str.substring(i)

String::substring

(String s) -> System.out.println(s)

System.out::println

() -> new Date()

Date::new

    b) 指向現有對象的實例方法的方法引用

 1 import java.util.Arrays;
 2 import java.util.List;
 3 
 4 import static java.util.Comparator.comparing;
 5 
 6 public class Test {
 7 
 8     public static void printApples(List<Apple> inventory) {
 9         for (Apple apple : inventory) {
10             System.out.println(apple);
11         }
12     }
13 
14     public static void main(String[] args) {
15         List<Apple> inventory = Arrays.asList(
16                 new Apple(100, "red"),
17                 new Apple(110, "red"),
18                 new Apple(190, "red"),
19                 new Apple(170, "red"),
20                 new Apple(100, "green"),
21                 new Apple(120, "green"),
22                 new Apple(160, "green"),
23                 new Apple(180, "green")
24         );
25         // i.e. inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
26         // i.e. inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));
27         inventory.sort(comparing(Apple::getWeight));
28         printApples(inventory);
29     }
30 
31 }

    c) 指向構造函數的方法引用

 1 import java.util.Date;
 2 import java.util.function.Function;
 3 import java.util.function.Supplier;
 4 
 5 interface TriFunction<T, U, V, R>{
 6     R apply(T t, U u, V v);
 7 }
 8 
 9 public class Test {
10 
11     public static void main(String[] args) {
12         Supplier<Date> s = Date::new; // i.e. () -> new Date()
13         Date d1 = s.get();
14         System.out.println(d1);
15 
16         Function<Long, Date> f = Date::new; // i.e. (Long l) -> new Date(l)
17         Date d2 = f.apply(0L);
18         System.out.println(d2);
19 
20         TriFunction<Integer, Integer, Integer, Date> tf = Date::new;
21         Date d3 = tf.apply(2000, 1, 1);
22         System.out.println(d3);
23     }
24 
25 }

複合Lambda表達式

1. 複合Lambda表達式:把多個簡單的Lambda表達式複合成複雜的表達式,好比使用and、or複合。

2. 舉例

    a) Comparator複合

 1 import java.util.Arrays;
 2 import java.util.List;
 3 
 4 import static java.util.Comparator.comparing;
 5 
 6 public class Test {
 7 
 8     public static void printApples(List<Apple> inventory) {
 9         for (Apple apple : inventory) {
10             System.out.println(apple);
11         }
12     }
13 
14     public static void main(String[] args) {
15         List<Apple> inventory = Arrays.asList(
16                 new Apple(100, "red"),
17                 new Apple(110, "red"),
18                 new Apple(190, "red"),
19                 new Apple(170, "red"),
20                 new Apple(100, "green"),
21                 new Apple(120, "green"),
22                 new Apple(160, "green"),
23                 new Apple(180, "green")
24         );
25         inventory.sort(
26                 comparing(Apple::getWeight)
27                 .reversed()
28                 .thenComparing(Apple::getColor)
29         );
30         printApples(inventory);
31     }
32 
33 }

    b) Predicate複合

 1 import java.util.ArrayList;
 2 import java.util.Arrays;
 3 import java.util.List;
 4 import java.util.function.Predicate;
 5 
 6 public class Test {
 7 
 8     public static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p) {
 9         List<Apple> result = new ArrayList<>();
10         for (Apple apple : inventory) {
11             if (p.test(apple)) {
12                 result.add(apple);
13             }
14         }
15         return result;
16     }
17 
18     public static void printApples(List<Apple> inventory) {
19         for (Apple apple : inventory) {
20             System.out.println(apple);
21         }
22     }
23 
24     public static void main(String[] args) {
25         List<Apple> inventory = Arrays.asList(
26                 new Apple(100, "red"),
27                 new Apple(110, "red"),
28                 new Apple(190, "red"),
29                 new Apple(170, "red"),
30                 new Apple(100, "green"),
31                 new Apple(120, "green"),
32                 new Apple(160, "green"),
33                 new Apple(180, "green")
34         );
35         Predicate<Apple> p = a -> "red".equals(a.getColor());
36         p = p.negate()
37                 .and(a -> a.getWeight() > 150)
38                 .or(a -> a.getWeight() <= 110);
39         List<Apple> newInventory = filterApples(inventory, p);
40         printApples(newInventory);
41     }
42 
43 }

    c) 函數複合

 1 import java.util.function.Function;
 2 
 3 public class Test {
 4 
 5     public static void main(String[] args) {
 6         Function<Integer, Integer> f = x -> x + 1;
 7         Function<Integer, Integer> g = x -> x * 2;
 8 
 9         Function<Integer, Integer> h1 = f.andThen(g); // i.e. g(f(x))
10         int result1 = h1.apply(1);
11         System.out.println(result1); // 4
12 
13         Function<Integer, Integer> h2 = f.compose(g); // i.e. f(g(x))
14         int result2 = h2.apply(1);
15         System.out.println(result2); // 3
16     }
17 
18 }

 

做者:netoxi
出處:http://www.cnblogs.com/netoxi本文版權歸做者和博客園共有,歡迎轉載,未經贊成須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。歡迎指正與交流。

相關文章
相關標籤/搜索