Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

3分钟看完Java 8——史上最强Java 8新特性总结之第一篇 函数式编程基础

未知 2019-02-18 10:55:00 阅读数:132 评论数:0 点赞数:0 收藏数:0

目录

· 行为参数化

· Lambda表达式

    · 概况

    · 函数式接口

    · 类型推断

    · 使用外层变量

    · 方法引用

    · 复合Lambda表达式

 

行为参数化

  1. 理解函数式编程要先理解行为参数化。
  2. 行为参数化:一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力。
  3. 行为参数化优点:可让代码更好地适应不断变化的需求,减轻未来的工作量。
  4. 实现方式

    a) Java 8以前:通过接口实现类或接口匿名类实现。

    b) Java 8及以后:通过Lambda表达式实现。

  1. 举例

    a) 通过接口实现类实现行为参数化

        i. Apple.java(后续举例将多次使用到该类)1 public classApple {2 3 privateInteger weight;4 5 privateString color;6 7 publicApple(Integer weight, String color) {8 this.weight =weight;9 this.color =color;10 }11 12 publicInteger getWeight() {13 returnweight;14 }15 16 public voidsetWeight(Integer weight) {17 this.weight =weight;18 }19 20 publicString getColor() {21 returncolor;22 }23 24 public voidsetColor(String color) {25 this.color =color;26 }27 28 @Override29 publicString toString() {30 return "weight=" + weight + " color=" +color;31 }32 }

        ii. ApplePredicate.java

1 public interfaceApplePredicate {2 3 booleantest(Apple apple);4 5 }

        iii. AppleHeavyWeightPredicate.java

1 public class AppleHeavyWeightPredicate implementsApplePredicate {2 3 public booleantest(Apple apple) {4 return apple.getWeight() > 150;5 }6 7 }

        iv. AppleGreenColorPredicate.java

1 public class AppleGreenColorPredicate implementsApplePredicate {2 3 public booleantest(Apple apple) {4 return "green".equals(apple.getColor());5 }6 7 }

        v. Test.java

1 importjava.util.ArrayList;2 importjava.util.Arrays;3 importjava.util.List;4 5 public classTest {6 7 public static List filterApples(Listinventory, ApplePredicate p) {8 List result = new ArrayList<>();9 for(Apple apple : inventory) {10 if(p.test(apple)) {11 result.add(apple);12 }13 }14 returnresult;15 }16 17 public static void printApples(Listinventory) {18 for(Apple apple : inventory) {19 System.out.println(apple);20 }21 }22 23 public static voidmain(String[] args) {24 List 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 newInventory1 = filterApples(inventory, newAppleHeavyWeightPredicate());35 printApples(newInventory1);36 System.out.println("-----");37 List newInventory2 = filterApples(inventory, newAppleGreenColorPredicate());38 printApples(newInventory2);39 }40 41 }

    b) 通过接口匿名类实现行为参数化

        i. ApplePredicate.java1 public interfaceApplePredicate {2 3 booleantest(Apple apple);4 5 }

        ii. Test.java

1 importjava.util.ArrayList;2 importjava.util.Arrays;3 importjava.util.List;4 5 public classTest {6 7 public static List filterApples(Listinventory, ApplePredicate p) {8 List result = new ArrayList<>();9 for(Apple apple : inventory) {10 if(p.test(apple)) {11 result.add(apple);12 }13 }14 returnresult;15 }16 17 public static void printApples(Listinventory) {18 for(Apple apple : inventory) {19 System.out.println(apple);20 }21 }22 23 public static voidmain(String[] args) {24 List 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 newInventory1 = filterApples(inventory, newApplePredicate() {35 @Override36 public booleantest(Apple apple) {37 return apple.getWeight() > 150;38 }39 });40 printApples(newInventory1);41 System.out.println("-----");42 List newInventory2 = filterApples(inventory, newApplePredicate() {43 @Override44 public booleantest(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) 简洁:无需像匿名类那样写很多模板代码。

  1. Lambda表达式语法

    a) 语法格式1(parameters) -> expression

    b) 语法格式2

(parameters) -> { statements; }

    c) 举例

场景

Lambda表达式 布尔表达式

(List 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.java1 public interfaceApplePredicate {2 3 booleantest(Apple apple);4 5 }

    b) Test.java

1 importjava.util.ArrayList;2 importjava.util.Arrays;3 importjava.util.List;4 5 public classTest {6 7 public static List filterApples(Listinventory, ApplePredicate p) {8 List result = new ArrayList<>();9 for(Apple apple : inventory) {10 if(p.test(apple)) {11 result.add(apple);12 }13 }14 returnresult;15 }16 17 public static void printApples(Listinventory) {18 for(Apple apple : inventory) {19 System.out.println(apple);20 }21 }22 23 public static voidmain(String[] args) {24 List 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 newInventory1 = filterApples(inventory, (Apple apple) -> apple.getWeight() > 150);35 printApples(newInventory1);36 System.out.println("-----");37 List 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或Function等通用函数式接口,而应使用IntPredicate、IntToLongFunction等原始类型特化接口。
  4. Java 8常用函数式接口
    函数式接口

函数描述符

原始类型特化 Predicate

T->boolean

IntPredicate

LongPredicate

DoublePredicate Consumer

T->void

IntConsumer

LongConsumer

DoubleConsumer Function

T->R

IntFunction

IntToDoubleFunction

IntToLongFunction

LongFunction

LongToDoubleFunction

LongToIntFunction

DoubleFunction

ToIntFunction

ToDoubleFunction

ToLongFunction Supplier

()->T

BooleanSupplier

IntSupplier

LongSupplier

DoubleSupplier UnaryOperator

T->T

IntUnaryOperator

LongUnaryOperator

DoubleUnaryOperator BinaryOperator

(T,T)->T

IntBinaryOperator

LongBinaryOperator

DoubleBinaryOperator BiPredicate

(L, R) -> boolean

  BiConsumer

(T, U) -> void

ObjIntConsumer

ObjLongConsumer

ObjDoubleConsumer BiFunction

(T, U) -> R

ToIntBiFunction

ToLongBiFunction

ToDoubleBiFunction

  1. 举例

    a) Lambda表达式与函数式接口对应场景

Lambda表达式

对应的函数式接口 布尔表达式

(List list) -> list.isEmpty()

Predicate> 创建对象

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

Supplier 消费一个对象

(Apple a) -> {

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

}

Consumer 从一个对象中选择/抽取

(String s) -> s.length()

Function

ToIntFunction 组合两个值

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

IntBinaryOperator 比较两个值

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

Comparator

BiFunction

ToIntBiFunction

    b) Predicate

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

    c) Consumer

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

    d) Function

1 importjava.util.ArrayList;2 importjava.util.Arrays;3 importjava.util.List;4 importjava.util.function.Function;5 6 public classTest {7 8 public static List map(List list, Functionf) {9 List result = new ArrayList<>();10 for(T s: list){11 result.add(f.apply(s));12 }13 returnresult;14 }15 16 public static voidmain(String[] args) {17 List listOfStrings = Arrays.asList("lambdas", "in", "action");18 List 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 c = () -> 42;2 PrivilegedAction p = () -> 42;

1 Comparator c1 = (Apple a1, Apple a2) ->a1.getWeight().compareTo(a2.getWeight());2 ToIntBiFunction c2 = (Apple a1, Apple a2) ->a1.getWeight().compareTo(a2.getWeight());3 BiFunction c3 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

  1. void兼容规则:如果一个Lambda的主体是一个语句表达式, 它就和一个返回void的函数描述符兼容(当然需要参数列表也兼容)。例如,虽然List.add方法返回boolean,但Consumer的T->void仍然兼容。
    Consumer b = s -> list.add(s);
  2. Object类:Object不是函数式接口。下面的代码无法编译。

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

  1. Lambda表达式类型省略

    a) 参数类型省略:省略和不省略都可能更易读。1 //没有类型推断 2 Comparator c = (Apple a1, Apple a2) ->a1.getWeight().compareTo(a2.getWeight());3 //有类型推断 4 Comparator c = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight());

    b) 参数括号省略:当Lambda仅有一个类型需要推断的参数时,参数名称两边的括号也可以省略。

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

使用外层变量

  1. 外层变量限制:Lambda表达式只能使用显式声明为final或实际上是final的外层变量。与匿名类类似,但匿名类更严格(只能使用显式声明为final的外层变量)。
  2. 限制原因

    a) Lambda表达式在访问外层变量时,实际上是在访问它的副本,而不是访问原始变量。

    b) 函数式编程不鼓励使用外层变量(更容易并行)。

  1. 举例

    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 newThread(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)的一些特殊形式的方法引用。

  1. 举例

    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 importjava.util.Arrays;2 importjava.util.List;3 4 import staticjava.util.Comparator.comparing;5 6 public classTest {7 8 public static void printApples(Listinventory) {9 for(Apple apple : inventory) {10 System.out.println(apple);11 }12 }13 14 public static voidmain(String[] args) {15 List 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 importjava.util.Date;2 importjava.util.function.Function;3 importjava.util.function.Supplier;4 5 interface TriFunction{6 R apply(T t, U u, V v);7 }8 9 public classTest {10 11 public static voidmain(String[] args) {12 Supplier s = Date::new; //i.e. () -> new Date() 13 Date d1 =s.get();14 System.out.println(d1);15 16 Function f = Date::new; //i.e. (Long l) -> new Date(l) 17 Date d2 = f.apply(0L);18 System.out.println(d2);19 20 TriFunction 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 importjava.util.Arrays;2 importjava.util.List;3 4 import staticjava.util.Comparator.comparing;5 6 public classTest {7 8 public static void printApples(Listinventory) {9 for(Apple apple : inventory) {10 System.out.println(apple);11 }12 }13 14 public static voidmain(String[] args) {15 List 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 importjava.util.ArrayList;2 importjava.util.Arrays;3 importjava.util.List;4 importjava.util.function.Predicate;5 6 public classTest {7 8 public static List filterApples(List inventory, Predicatep) {9 List result = new ArrayList<>();10 for(Apple apple : inventory) {11 if(p.test(apple)) {12 result.add(apple);13 }14 }15 returnresult;16 }17 18 public static void printApples(Listinventory) {19 for(Apple apple : inventory) {20 System.out.println(apple);21 }22 }23 24 public static voidmain(String[] args) {25 List 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 p = a -> "red".equals(a.getColor());36 p =p.negate()37 .and(a -> a.getWeight() > 150)38 .or(a -> a.getWeight() <= 110);39 List newInventory =filterApples(inventory, p);40 printApples(newInventory);41 }42 43 }

    c) 函数复合

1 importjava.util.function.Function;2 3 public classTest {4 5 public static voidmain(String[] args) {6 Function f = x -> x + 1;7 Function g = x -> x /* 2;8 9 Function h1 = f.andThen(g); //i.e. g(f(x)) 10 int result1 = h1.apply(1);11 System.out.println(result1); //4 12 13 Function 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本文版权归作者和博客园共有,欢迎转载,未经同意须保留此段声明,且在文章页面明显位置给出原文连接。欢迎指正与交流。

 

版权声明
本文为[netoxi]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/netoxi/p/10346535.html

编程之旅,人生之路,不止于编程,还有诗和远方。
阅代码原理,看框架知识,学企业实践;
赏诗词,读日记,踏人生之路,观世界之行;

支付宝红包,每日可领