目录
· 行为参数化
· 概况
· 函数式接口
· 类型推断
· 使用外层变量
· 方法引用
行为参数化
1. 理解函数式编程要先理解行为参数化。
2. 行为参数化:一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力。
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) 举例
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常用函数式接口
5. 举例
a) Lambda表达式与函数式接口对应
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表达式与方法引用对应
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
本文版权归作者和博客园共有,欢迎转载,未经同意须保留此段声明,且在文章页面明显位置给出原文连接。欢迎指正与交流。