文档章节

行为参数化与lambda表达式 - 读《Java 8实战》

yysue
 yysue
发布于 2018/08/12 15:11
字数 2013
阅读 65
收藏 6

零、 概述

第一部分:1~3章 主要讲了行为参数化和Lambda表达式

第二部分:4~7章 主要讲了流的应用,包括流与集合差异,流的操作,收集器,注的并行执行

第三部分:8~12章 主要讲了怎样用Java8引入的特性改善老代码,Optional类和CompleteFuture及新的日期和时间API

第四部分:13~16章 主要讲了函数式编程

本文主要是对第一部分的笔记。

一、行为参数化

1.1 行为参数化定义

行为参数化就是拿出一个代码块,把它准备好却不去执行它。

1.2 举例:需求

有个果农,有如下需求:

  1. 从仓库中找到红色苹果
  2. 找出所有重量超过150g的

扩展一下:

  1. 可能以后还需要查找绿色苹果的功能
  2. 可能还需要找重量超过200g的

1.3 举例:方案1

传统实现方案

// 筛选绿色苹果
public static List<Apple> filterGreenApples(List<Apple> inventory) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if ("green".equals(apple.getColor())) {
            result.add(apple);
        }
    }
    return result;
}

// 可筛选任意颜色苹果,把颜色作为参数
public static List<Apple> filterGreenApplesByColor(List<Apple> inventory, String color) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (apple.getColor().equals(apple.getColor())) {
            result.add(apple);
        }
    }
    return result;
}
// 筛选不同重量的苹果
public static List<Apple> filterGreenApplesByWeight(List<Apple> inventory, int weight) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (apple.getWeight() > weight) {
            result.add(apple);
        }
    }
    return result;
}

// 写一个方法同时支持筛选颜色和重量
public static List<Apple> filterGreenApples(List<Apple> inventory, String color, int weight
            , boolean filterColorFlag) {
	List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if ((filterColorFlag && apple.getColor().equals(color))
                || (!filterColorFlag && apple.getWeight() > weight)) {
            result.add(apple);
        }
    }
	return result;
}

1.4 举例:方案2

使用对象传递行为参数

interface ApplePredicate {
    // 一个返回boolea值的函数,把它称为谓词
    boolean test(Apple apple);
}

// 筛选绿色
public class AppleGreenColorPredicate implements ApplePredicate {
    @Override
    public boolean test(Apple apple) {
        return "green".equals(apple.getColor());
    }
}

// 重量大于150
class AppleHeavyWeightPredicate implements ApplePredicate {
    @Override
    public boolean test(Apple apple) {
        return apple.getWeight() > 150;
    }
}

// 红色且重量大于150
class AppleRedAndHeavyPredicate implements ApplePredicate {
    @Override
    public boolean test(Apple apple) {
        return "red".equals(apple.getColor()) && apple.getWeight() > 150;
    }
}

// 实现
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (p.test(apple)) {
            result.add(apple);
        }
    }
    return result;
}

public void test() {
    List<Apple> inventory = new ArrayList<>();
    // 筛选绿色
    filterApples(inventory, new AppleGreenColorPredicate());

    // 重量大于150
    filterApples(inventory, new AppleHeavyWeightPredicate());

    // 红色且重量大于150
    filterApples(inventory, new AppleRedAndHeavyPredicate());
}

1.5 举例:方案3

使用匿名类传递行为参数

// 对选择标准建模
interface ApplePredicate {
    // 一个返回boolea值的函数,把它称为谓词
    boolean test(Apple apple);
}

// 实现
public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (p.test(apple)) {
            result.add(apple);
        }
    }
    return result;
}

public static void main(String[] args) {
    List<Apple> inventory = new ArrayList<>();
    // 筛选绿色
    filterApples(inventory, new ApplePredicate() {
        @Override
        public boolean test (Apple apple){
            return "green".equals(apple.getColor());
        }
    });

    // 重量大于150
    filterApples(inventory, new ApplePredicate() {
        @Override
        public boolean test (Apple apple){
            return apple.getWeight() > 150;
        }
    });

    // 红色且重量大于150
    filterApples(inventory, new ApplePredicate() {
        @Override
        public boolean test (Apple apple){
            return "red".equals(apple.getColor()) && apple.getWeight() > 150;
        }
    });
}

1.6 举例:方案4

使用Lambda表达式传递行为参数

interface ApplePredicate {
    boolean test(Apple apple);
}

public static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : inventory) {
        if (p.test(apple)) {
            result.add(apple);
        }
    }
    return result;
}

public static void main(String[] args) {
    List<Apple> inventory = new ArrayList<>();
    // 筛选绿色
    filterApples(inventory
		, (Apple apple) -> "green".equals(apple.getColor()));

    // 重量大于150
    filterApples(inventory
		, (Apple apple) -> apple.getWeight() > 150);

    // 红色且重量大于150
    filterApples(inventory
		, (Apple apple) -> "red".equals(apple.getColor()) && apple.getWeight() > 150);
}

在这里小结一下:

2018-08-11_041102.png

1.7 举例:方案5

在方案4的基础上 将List类型抽象化

// 定义一个函数式接口
interface Predicate<T> {
    boolean test(T t);
}

// 定义一个调用函数式接口的方法
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
    List<T> result = new ArrayList<>();
    for (T e : list) {
        if (p.test(e)) {
            result.add(e);
        }
    }
    return result;
}

// 使用
public static void main(String[] args) {
    List<Apple> inventory = FakeDb.getApples();
    List<Apple> redList = Filtering.filter(inventory
		, (Apple apple) -> "red".equals(apple.getColor()));

    List<String> nonEmptyList = filter(Arrays.asList("1", "", "2")
		, (String s) -> !s.isEmpty());
}

二、Lambda表达式

2.1 Lambda表达式定义

简洁地表示可传递的匿名函数的一种方法。

  • 匿名
  • 函数
  • 传递
  • 简洁

2.2 Lambda表达式长什么样子?

下面是5个有效的Lambda表达式

// 1 参数是String s,返回值是int
(String s) -> s.length()

// 2 参数是Apple a,返回值是boolean
(Apple a) -> a.getWeight() > 150

//  3 参数是int x,int y 没有返回值 {}内放语句,怎样区分语句与表达式
(int x, int y) -> {
    System.out.println("Result:");
    System.out.println(x + y);
}

// 4 无参数,返回int
() -> 42

// 5 参数是两个Apple类型的变量,返回值是boolean
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())

2.3 函数式接口

函数式接口就是只定义一个抽象方法的接口。

函数式接口的抽象方法的签名基本上就是Lambda表达式的签名,这种抽象方法叫做函数描述符

一个注解:@FunctionalInterface,不是必须的,用于表示该接口会设计成一个函数式接口

2.4 Lambda的使用

Predicate 过滤掉列表中的空串

// 定义一个函数式接口
interface Predicate<T> {
    boolean test(T t);
}

// 定义一个调用函数式接口的方法
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
    List<T> result = new ArrayList<>();
    for (T e : list) {
        if (p.test(e)) {
            result.add(e);
        }
    }
    return result;
}

// 使用
public static void main(String[] args) {
    List<String> nonEmptyList = filter(Arrays.asList("1", "", "2")
		, (String s) -> !s.isEmpty());
}

Consumer 计算列表中的每个元素的平方并输出

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

public static <T> void forEach(List<T> list, Consumer<T> c) {
    for (T i : list) {
        c.accept(i);
    }
}

public static void main(String[] args) {
    forEach(Arrays.asList(1, 2, 3, 4), (Integer i) -> System.out.println(i * i));
}

Function 返回列表中每个元素的长度

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

public static <T, R> List<R> map(List<T> list, Function<T, R> f) {
    List<R> result = new ArrayList<>();
    for (T s : list) {
        result.add(f.apply(s));
    }
    return result;
}

public static void main(String[] args) {
    List<Integer> result = map(Arrays.asList("1", "22", "333")
		, (String s) -> s.length());
}

2.5 类型推断

List<Apple> l = new ArrayList<Apple>();
List<Apple> l = new ArrayList<>();

// Java编译器根据Lambda出现的上下文来推断Lambda表达式参数的类型
Predicate<Apple> p = (Apple a) -> 'red'.equals(a.getColor())
Predicate<Apple> p = a -> 'red'.equals(a.getColor())

2.6 方法引用

主要为了简化代码

方法引用,3种

  • 指向静态方法的方法引用 String.parseInt()
  • 指向实例方法的方法引用 str.length()
  • 指向外部对象实例方法的方法引用 globalVar.instanceMethod()
List<String> strList = Arrays.asList("a", "b", "A", "B");
strList.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
strList.sort(String::compareToIgnoreCase); // 等效的方法引用

构造函数引用

Supplier<Apple> c1 = Apple::new; // 指向Apply()构造函数
Apple a1 = c1.get();

Function<Integer, Apple> c2 = Apple::new; // 指向Apply(int weight)构造函数
Apple a2 = c2.apply(110);

BigFunction<String, Integer, Apple> c3 = Apple::new;
// 指向Apply(String color, Integer weight)
Apple c3 = c3.apply("green", 110);

2.7 Lambda实战

根据Apple的重量来排序

// 行为参数化,下面是通过不同方式传递这个行为的
// 1.使用对象
public class AppleComparator implements Comparator<Apple> {
    public int compare(Apple a1, Apple a2) {
        return a1.getWeight().compareTo(a2.getWeight());
    }
}
inventory.sort(new AppleComparator());

// 2.使用匿名类
inventory.sort(new Comparator<Apple>(){
    public int compare(Apple a1, Apple a2) {
        return a1.getWeight().compareTo(a2.getWeight());
    }
});
    
// 3.使用Lambda表达式
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
// 因为类型推断,可以简化成
inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));
// 因为有个java.util.Comparator.comparing静态方法,还可以简化成
import static java.util.Comparator.comparing;
inventory.sort(comparing((a) -> a.getWeight()));

// 4.使用方法引用
inventory.sort(comparing(comparing(Apple::getWeight)));

2.8 复合Lambda表达式

比较器复合

// 逆序,苹果按重量递减排序
inventory.sort(comparing(Apple::getWeight).reversed());

// 比较器链,先按重量递减排序再按国家排序
inverntory.sort(comparing(Apple::getWeight).reversed()
               .thenComparing(Apple::getCountry));

谓词复合

// negate,and,or
// 筛选不是红苹果
Predicate<Apple> notRedApple = redApple.negate();
// 筛选红苹果且重量大于150   或  绿苹果
redApple.and(a -> a.getWeight() > 150).or(a -> "green".equals(a.getColor()));

// a.or(b).and(c) <==> (a || b) && c

函数复合

// andThen,compose

Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;

// g(f(x))
Function<Integer, Integer> h = f.andThen(g);
int result = h.apply(1);

// f(g(x))
Function<Integer, Integer> h = f.compose(g);
int result = h.apply(1);

三、代码

https://gitee.com/yysue/tutorials-java/tree/master/java-8

© 著作权归作者所有

上一篇: arts-week4
yysue
粉丝 29
博文 273
码字总数 157915
作品 0
济南
程序员
私信 提问
Java 8的核心新特性:Lambda(匿名函数)、流、默认方法

Java 中的函数 Java 8中新增了函数——值的一种新形式。函数作为一等值,使用方法引用 :: 语法(即“把这个方法作为值”),作为函数式值来传递。 只要方法中有代码(方法中的可执行部分),...

Lienson
05/21
189
0
Effective Java 第三版——42.lambda表达式优于匿名类

Tips 《Effective Java, Third Edition》一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将近8年的时间,但随着Java 6,7,8...

M104
2018/05/16
0
0
Java 8 Lambda 表达式:模拟 Mixin 实现类的多重继承

Java 8 开始出现,带来一个全新特性:使用 Lambda 表达式 (JSR-335) 进行函数式编程。今天我们要讨论的是 Lambda 的其中一部分:虚拟扩展方法,也叫做公共辩护(defender)方法。该特性可以让你...

红薯
2012/08/15
6.9K
29
Java 8 和 Scala 中的 Lambda 表达式

这篇文章是infoQ上面关于java 8 和 scala 特性比较的一部分,原文实在是太长了,,分段翻译,先贴出来一部分,翻译的不好,欢迎拍砖。。。原文地址 Java8 终于要支持Lambda表达式!自2009年以...

散装海盗
2012/07/11
2.1K
0
vavr:让你像写Scala一样写Java

Hystrix是Netflix开源的限流、熔断降级组件,去年发现Hystrix已经不再更新了,而在github主页上将我引导到了另一个替代项目——resilience4j,这个项目是基于Java 8开发的,并且只使用了vav...

javaadu
06/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Spring boot 静态资源访问

0. 两个配置 spring.mvc.static-path-patternspring.resources.static-locations 1. application中需要先行的两个配置项 1.1 spring.mvc.static-path-pattern 这个配置项是告诉springboo......

moon888
今天
3
0
hash slot(虚拟桶)

在分布式集群中,如何保证相同请求落到相同的机器上,并且后面的集群机器可以尽可能的均分请求,并且当扩容或down机的情况下能对原有集群影响最小。 round robin算法:是把数据mod后直接映射...

李朝强
今天
4
0
Kafka 原理和实战

本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/bV8AhqAjQp4a_iXRfobkCQ 作者简介:郑志彬,毕业于华南理工大学计算机科学与技术(双语班)。先后从事过电子商务、开放平...

vivo互联网技术
今天
20
0
java数据类型

基本类型: 整型:Byte,short,int,long 浮点型:float,double 字符型:char 布尔型:boolean 引用类型: 类类型: 接口类型: 数组类型: Byte 1字节 八位 -128 -------- 127 short 2字节...

audience_1
今天
9
0
太全了|万字详解Docker架构原理、功能及使用

一、简介 1、了解Docker的前生LXC LXC为Linux Container的简写。可以提供轻量级的虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性。相当于C++中的NameSpa...

Java技术剑
今天
27
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部