函数式编程,Lambda表达式
Java8最大的变化是引入了函数式编程(functional programming)。具体增加了
- Lambda表达式(Lambda Experssion)
- 方法引用(Method Reference)
- 流(Stream)
本文主要内容为,函数式编程入门和Lambda表达式。
函数式接口:有且只有一个抽象方法的接口。用来表示Lambda表达式的类型。
函数作为一等公民
- 可以被定义
- 可以作为参数
- 可以作为返回值
Java8新增了注解@FunctionalInterface,用于标识一个接口为函数式接口。
接口新特性
- 接口支持默认方法
支持default关键字
1 @FunctionalInterface 2 public interface Consumer<T> { 3 4 void accept(T t); 5 6 default Consumer<T> andThen(Consumer<? super T> after) { 7 Objects.requireNonNull(after); 8 return (T t) -> { accept(t); after.accept(t); }; 9 } 10 }
- 接口支持静态方法
支持static修饰方法体
1 @FunctionalInterface 2 public interface UnaryOperator<T> extends Function<T, T> { 3 4 static <T> UnaryOperator<T> identity() { 5 return t -> t; 6 } 7 }
Lambda表达式语法:
- 参数
- 箭头符号
- 主体
Lambda表达式形式
Runnable noArguments = () -> System.out.println("Hello World"); | 无参数,无返回值。 |
ActionListener oneArgument = event -> System.out.println("button clicked"); |
有参数,无返回值。只有一个参数可以省略参数括号 |
Runnable multiStatement = () -> { System.out.println("Hello"); System.out.println(" World"); } |
无参数,无返回值。有多个表达式语句,用大括号括起来。 |
BinaryOperator<Long> add = (x, y) -> x +y; |
多个参数,有返回值。参数无显式类型。 |
BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y; |
参数显式声明。 |
Java8新增函数式接口
接口 | 参数 | 返回类型 |
Predicate<T> | T | boolean |
Consumer<T> | T | void |
Function<T, R> | T | R |
Supplier<T> | None | T |
UnaryOperator<T> | T | T |
BinaryOperator<T> | (T, T) | T |
其他内置函数式接口
接口 | 参数 | 返回类型 |
Runnable | None | void |
Callable<V> | None | V |
Comparator<T> | T | int |
FileFilter | File | boolean |
Comparable<T> | T | int |
数据和行为分离
复合Lambda表达式
- 比较器复合
逆序
1 Comparator.comparing(String::length).reversed();
比较器链
1 Comparator.comparing(String::length).thenComparing(Comparator.naturalOrder());
- 谓词复合
包含三个方法:negate、and、or。
1 Predicate<Person> isMale = Person::isMale; 2 3 Predicate<Person> notMale = isMale.negate(); 4 Predicate<Person> isMaleAndIsChildren = isMale.and(Person::isChildren); 5 Predicate<Person> isMaleOrIsChildren = isMale.or(Person::isChildren);
- 函数复合
andThen、compose
1 UnaryOperator<Integer> u1 = x -> x + 1; 2 UnaryOperator<Integer> u2 = x -> x * 2; 3 4 // 先执行u1,再执行u2 5 Function<Integer, Integer> f1 = u1.andThen(u2); 6 // 先执行u2,再执行u1 7 Function<Integer, Integer> f2 = u1.compose(u2); 8 9 f1.apply(1); // (1 + 1) * 2 = 4 10 11 f2.apply(1); // (1 * 2) + 1 = 3
Java内置函数复合
复杂的复合函数
实现复杂的函数调用
代码演示1:
1 /** 2 * 3 * @param treeTableView 树视图 4 * @param keyMapper 键映射 5 * @param <T> 泛型类型 6 * @return 键为外层节点,值为是否展开的映射 7 */ 8 public static <T> Map<String, Boolean> getExpands(TreeTableView<T> treeTableView, Function<T, String> keyMapper) { 9 if(treeTableView.getRoot() == null) { 10 return Collections.emptyMap(); 11 } 12 Map<String, Boolean> expands = treeTableView.getRoot().getChildren().stream().collect(Collectors.toMap(keyMapper.compose(TreeItem::getValue), TreeItem::isExpanded)); 13 return expands; 14 }
代码演示2:
1 * 2 * @param treeTableView 树视图 3 * @param data 泛型数组 4 * @param classifier 分类符 5 * @param keyMapper 键映射 6 * @param clazz 泛型类型 7 * @param <T> 参数化类型 8 */ 9 public static <T> void setDataToTreeTableAndKeepSelect(TreeTableView<T> treeTableView, String data, Function<T, String> classifier, Function<T, String> keyMapper, Class<T> clazz, boolean flag) { 10 try { 11 Map<String, Boolean> expands = getExpands(treeTableView, keyMapper); 12 List<T> list = JSONObject.parseArray(data, clazz); 13 Map<String, List<T>> grouped = list.stream().collect(Collectors.groupingBy(classifier, LinkedHashMap::new, Collectors.toList())); 14 TreeItem<T> selectedItem = treeTableView.getSelectionModel().getSelectedItem(); 15 TreeItem<T> root = new TreeItem<>(clazz.newInstance()); 16 treeTableView.setRoot(root); 17 for (Map.Entry<String, List<T>> entryOuter : grouped.entrySet()) { 18 Constructor<T> constructor = clazz.getConstructor(String.class, NodeClass.class); 19 T outer = constructor.newInstance(entryOuter.getKey(), NodeClass.OUTER); 20 TreeItem<T> outerTreeItem = new TreeItem<>(outer); 21 root.getChildren().add(outerTreeItem); 22 outerTreeItem.setExpanded(expands.getOrDefault(entryOuter.getKey(), false)); 23 for (T inner : entryOuter.getValue()) { 24 TreeItem<T> innerTreeItem = new TreeItem<>(inner); 25 outerTreeItem.getChildren().add(innerTreeItem); 26 } 27 } 28 if (flag) { 29 treeTableView.getRoot().getChildren().forEach(treeItem -> treeItem.setExpanded(true)); 30 } 31 treeTableView.setShowRoot(false); 32 treeTableView.getSelectionModel().clearSelection(); 33 if (selectedItem == null) { 34 treeTableView.getSelectionModel().select(0); 35 } else { 36 treeTableView.getSelectionModel().select(selectedItem); 37 } 38 } catch (Exception e) { 39 e.printStackTrace(); 40 } 41 }
优化后的版本,移除反射:
1 import javafx.scene.control.TreeItem; 2 import javafx.scene.control.TreeTableView; 3 4 import java.util.*; 5 import java.util.function.BiFunction; 6 import java.util.function.Function; 7 import java.util.function.Supplier; 8 import java.util.stream.Collectors; 9 10 public class Demo { 11 12 public static void main(String[] args) { 13 /** 14 * 模拟方法调用 15 */ 16 setDataToTreeTableAndKeepSelect(new TreeTableView<>(), Arrays.asList(new Entry()), Entry::getClassifier, Entry::getKey, Entry::new, Entry::new, true); 17 } 18 19 /** 20 * @param treeTableView 需要挂在的树 21 * @param data 数据链表 22 * @param classifier Entry::getMap 获取分组标识 23 * @param keyMapper Entry::getKey 获取唯一键,用来判断节点历史展开情况 24 * @param biFunction Entry::new 调用有参构造函数 25 * @param supplier Entry::new 调用无参构造函数 26 * @param flag 节点是否展开 27 * @param <T> 泛型类 28 */ 29 public static <T> void setDataToTreeTableAndKeepSelect(TreeTableView<T> treeTableView, List<T> data, Function<T, String> classifier, Function<T, String> keyMapper, BiFunction<String, NodeClass, T> biFunction, Supplier<T> supplier, boolean flag) { 30 Map<String, Boolean> expands = getExpands(treeTableView, keyMapper); 31 Map<String, List<T>> grouped = data.stream().collect(Collectors.groupingBy(classifier, LinkedHashMap::new, Collectors.toList())); 32 TreeItem<T> selectedItem = treeTableView.getSelectionModel().getSelectedItem(); 33 TreeItem<T> root = new TreeItem<>(supplier.get()); 34 treeTableView.setRoot(root); 35 for (Map.Entry<String, List<T>> entryOuter : grouped.entrySet()) { 36 T outer = biFunction.apply(entryOuter.getKey(), NodeClass.OUTER); 37 TreeItem<T> outerTreeItem = new TreeItem<>(outer); 38 root.getChildren().add(outerTreeItem); 39 outerTreeItem.setExpanded(expands.getOrDefault(entryOuter.getKey(), false)); 40 for (T inner : entryOuter.getValue()) { 41 TreeItem<T> innerTreeItem = new TreeItem<>(inner); 42 outerTreeItem.getChildren().add(innerTreeItem); 43 } 44 } 45 if (flag) { 46 treeTableView.getRoot().getChildren().forEach(treeItem -> treeItem.setExpanded(true)); 47 } 48 treeTableView.setShowRoot(false); 49 treeTableView.getSelectionModel().clearSelection(); 50 if (selectedItem == null) { 51 treeTableView.getSelectionModel().select(0); 52 } else { 53 treeTableView.getSelectionModel().select(selectedItem); 54 } 55 } 56 57 /** 58 * @param treeTableView 当前树 59 * @param keyMapper 键映射器 60 * @param <T> 泛型类 61 * @return 当前节点展开、折叠记录 62 */ 63 public static <T> Map<String, Boolean> getExpands(TreeTableView<T> treeTableView, Function<T, String> keyMapper) { 64 if (treeTableView.getRoot() == null) { 65 return Collections.emptyMap(); 66 } 67 Map<String, Boolean> expands = treeTableView.getRoot().getChildren().stream().collect(Collectors.toMap(keyMapper.compose(TreeItem::getValue), TreeItem::isExpanded)); 68 return expands; 69 } 70 71 /** 72 * 树装载的对象类 73 */ 74 public static class Entry { 75 private String key; 76 private String classifier; 77 private NodeClass nodeClass; 78 79 public Entry() { 80 } 81 82 public Entry(String key, NodeClass nodeClass) { 83 this.key = key; 84 this.nodeClass = nodeClass; 85 } 86 87 public String getKey() { 88 return key; 89 } 90 91 public void setKey(String key) { 92 this.key = key; 93 } 94 95 public String getClassifier() { 96 return classifier; 97 } 98 99 public void setClassifier(String classifier) { 100 this.classifier = classifier; 101 } 102 103 public NodeClass getNodeClass() { 104 return nodeClass; 105 } 106 107 public void setNodeClass(NodeClass nodeClass) { 108 this.nodeClass = nodeClass; 109 } 110 } 111 112 /** 113 * 三层节点层级 114 */ 115 public enum NodeClass { 116 OUTER, 117 MID, 118 INNER; 119 } 120 }