Featured image of post Lambda表达式&Stream流-函数式编程

Lambda表达式&Stream流-函数式编程

(java8)Lambda表达式&Stream流-函数式编程

概述

优点:

  • 大数量下处理集合效率高
  • 代码可读性高
  • 消灭嵌套地狱

Lambda表达式

概述

lambda是jdk8的语法糖,可以对某些匿名内部类的写法进行简化

基本格式

1
(参数列表)->{代码}

例1

建线程的时候会用到匿名的内部类的创建和方法重写

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class lambda_01 {
    public static void main(String[] args) {
        new Thread(new Runnable(){
            @Override
            public void run(){
                System.out.println("hello");
            }
        }).start();
    }
}

可以用lambda表达式简化重写

1
2
3
4
5
public class lambda_01 {
    public static void main(String[] args) {
        new Thread(() -> System.out.println("hello")).start();
    }
}

例2

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import java.util.function.IntBinaryOperator;

public class lambda_01 {
    public static void main(String[] args) {
        calculateNum(new IntBinaryOperator() {
            @Override
            public int applyAsInt(int left, int right) {
                System.out.println(left+right);
                return 0;
            }
        });

    }
    public static int calculateNum(IntBinaryOperator op){
        int a = 10;
        int b = 20;
        return op.applyAsInt(a, b);
    }
}

lambda

1
2
3
4
calculateNum((left, right) -> {
            System.out.println(left+right);
            return 0;
        });

例3

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.function.Function;
import java.util.function.IntBinaryOperator;

public class lambda_01 {
    public static void main(String[] args) {
        typeConver(new Function<String, Integer>() {

            @Override
            public Integer apply(String s) {
                System.out.println(Integer.valueOf(s));
                return 0;
            }
        });

    }
    public static <R> R typeConver(Function<String, R> func){
        String str = "114514";
        R result = func.apply(str);
        return result;
    }
}

lambda

1
2
3
4
typeConver(s -> {
            System.out.println(Integer.valueOf(s));
            return 0;
        });

省略规则

  • 参数类型可以省略
  • 方法体只有一句话时大括号return和唯一一句代码分号可以省略
  • 方法只有一个参数小括号可以省略

Stream流

概述

可以用来对集合或数组进行链状流式的操作

案例demo

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package Stream;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;


import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Author {
    private Long id;
    private String name;
    private Integer age;
    private String intro;
    private List<Book> books;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package Stream;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Book {
    private Long id;
    private String name;
    private String category;
    private Integer score;
    private String intro;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package Stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;


public class Test01 {
    private static List<Author> getAuthors() {
        Author author1 = new Author(1L, "雷蒙多", 33, "简介1", null);
        Author author2 = new Author(2L, "亚拉索", 15, "简介2", null);
        Author author3 = new Author(3L, "易", 14, "简介3", null);
        Author author4 = new Author(3L, "易", 14, "简介3", null);

        List<Book> books1 = new ArrayList<>();
        List<Book> books2 = new ArrayList<>();
        List<Book> books3 = new ArrayList<>();

        books1.add(new Book(1L, "刀的两侧是光明与黑暗", "哲学,爱情", 88, "用一把刀划分了爱恨"));
        books1.add(new Book(2L, "一个人不能死在同一把刀下", "个人成长,爱情", 99, "讲述如何从失败中明悟真理"));

        books2.add(new Book(3L, "那风吹不到的地方", "哲学", 85, "带你用思维去领略世界的尽头"));
        books2.add(new Book(3L, "那风吹不到的地方", "哲学", 85, "带你用思维去领略世界的尽头"));
        books2.add(new Book(4L, "吹或不吹", "爱情,个人传记", 56, "一个哲学家的恋爱观注定很难把他所在的时代理解"));

        books3.add(new Book(5L, "你的剑就是我的剑", "爱情", 56, "无法想象一个武者能对他的伴侣这么的宽容"));
        books3.add(new Book(6L, "风与剑", "个人传记", 100, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?"));
        books3.add(new Book(6L, "风与剑", "个人传记", 100, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?"));
        author1.setBooks(books1);
        author2.setBooks(books2);
        author3.setBooks(books3);
        author4.setBooks(books3);

        return new ArrayList<>(Arrays.asList(author1, author2, author3, author4));
    }
}

快速入门

需求

调用getAuthors方法获取元素集合,打印所有小于18岁的作家名字,并且去重

实现

1
2
3
4
5
6
7
public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream()//将集合转换为流
                .distinct()
                .filter(author -> author.getAge() < 18)
                .forEach(author -> System.out.println(author.getName()));
    }

常用操作

创建流

Java 中有两类集合,一类是单列集合,父接口为Collection,一类是双列集合,父接口为Map。根据集合对象的不同,有如下几种创建流的方式:

单列集合(List、Set):集合对象.stream()

1
2
List<Author> authors = getAuthors();
Stream<Author> stream = authors.stream();

数组([]):Arrays.stream(数组) 或者 Stream.of(数组)

1
2
3
Integer[] arr = {1, 2, 3, 4, 5};
Stream<Integer> stream1 = Arrays.stream(arr);
Stream<Integer> stream2 = Stream.of(arr);

双列集合:转换为单列集合后再创建

1
2
3
4
5
6
Map<String, Integer> map = new HashMap<>();
map.put("xiaoxin", 19);
map.put("ameng", 17);
map.put("wukong", 16);

Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();

中间操作

filter
1
2
3
4
5
6
7
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    authors.stream()
            .filter(author -> author.getName().length() > 1)
            .forEach(author -> System.out.println(author.getName()));
}
map
1
2
3
4
5
6
7
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    authors.stream()
            .map(author -> author.getName())
            .forEach(name -> System.out.println(name));
}

当然,其实这个需求单独用forEach也可以实现。但是经过map操作之后,流中的数据类型会改变,一定程度上减轻了数据量级。

1
2
3
4
5
6
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    authors.stream()
            .forEach(author -> System.out.println(author.getName()));
}
distinct

distinct方法是依赖类中的equals方法来判断是否是相同对象的,所以如果要对某个类型的对象进行去重,这个类中必须重写equals() 和 hashCode() 方法。

1
2
3
4
5
6
7
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    authors.stream()
            .distinct()
            .forEach(author -> System.out.println(author.getName()));
}
sorted
1
2
3
4
5
6
7
8
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    authors.stream()
            .distinct()
            .sorted()
            .forEach(author -> System.out.println(author.getAge()));
}

降序

1
2
3
4
5
6
7
8
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    authors.stream()
            .distinct()
            .sorted((o1, o2) -> o2.getAge() - o1.getAge()) // 降序排列
            .forEach(author -> System.out.println(author.getAge()));
}
limit
1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    authors.stream()
            .distinct()
            .sorted((o1, o2) -> o2.getAge() - o1.getAge()) // 降序排列
            .limit(2)
            .forEach(author -> System.out.println(author.getAge()));
}
skip
1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    authors.stream()
            .distinct()
            .sorted((o1, o2) -> o2.getAge() - o1.getAge()) // 降序排列
            .skip(1)
            .forEach(author -> System.out.println(author.getAge()));
}
flatMap

map 只能把一个对象转换成另一个对象来作为流中的元素,而flatMap可以把一个对象转换成多个对象作为流中的元素。使用flatMap(),将流中列表类型的元素转换为新的流,新流中包含的就是列表中的元素,再使用distinct去重时,去重的对象就是Book对象了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    // 匿名内部类形式
    authors.stream()
            .flatMap(new Function<Author, Stream<?>>() {
                @Override
                public Stream<?> apply(Author author) {
                    return author.getBooks().stream();
                }
            })
            .distinct()
            .forEach(System.out::println);

    // Lambda
    authors.stream()
            .flatMap(author -> author.getBooks().stream())
            .distinct()
            .forEach(System.out::println);
}

结尾操作

必须要有结尾操作,中间操作才会被调用到,进而生效,否则中间操作不会被执行

forEach

对流中的元素进行遍历操作,可以通过传入的参数指定对遍历到的元素进行什么具体操作。

count

获取当前流中元素的个数。

max&min
  • max:通过传入的Comparator对元素进行比较,得到最大值;
  • min:通过传入的Comparator对元素进行比较,得到最小值。

Comparator的实现方法和 sorted() 中一致。

此外,max和min返回的是一个Optional对象,需要通过 get() 获取到原始对象才可以使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    Optional<Book> max = authors.stream()
            .flatMap(author -> author.getBooks().stream())
            .distinct()
            .max((o1, o2) -> o1.getScore() - o2.getScore());
    if (max.isPresent()) {
        Book book = max.get();
        System.out.println(book.getScore());
    }
    // 上面对max进行判断与输出的代码,也可以简化为Lambda表达式如下
    max.ifPresent(book -> System.out.println(book.getScore()));

    // 因为这里最后只需要输出分数,因此当我们取到 Stream<Book> 流对象后
    // 可以将流转换为 Stream<Integer> 流,只包含分数就可以,降低数据量级
    Optional<Integer> maxed = authors.stream()
            .flatMap(author -> author.getBooks().stream())
            .distinct()
            .map(book -> book.getScore())
            .max((o1, o2) -> o1 - o2);
    maxed.ifPresent(score -> System.out.println(score));
}
collect

在某些场景下,集合通过流处理之后,需要导出为一个新的集合进行使用,这时候就需要使用 collect() 方法。

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    List<String> nameList = authors.stream()
            .map(author -> author.getName())
            .collect(Collectors.toList());

    System.out.println(nameList);
}

toMap

由于 toMap() 的匿名内部类比较复杂,先给出匿名内部类的方式,便于理解原理。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    Map<String, List<Book>> books = authors.stream()
            .collect(Collectors.toMap(new Function<Author, String>() {
                @Override
                public String apply(Author author) {
                    return author.getName();
                }
            }, new Function<Author, List<Book>>() {
                @Override
                public List<Book> apply(Author author) {
                    return author.getBooks();
                }
            }, new BinaryOperator<List<Book>>() {
                @Override
                public List<Book> apply(List<Book> books1, List<Book> books2) {
                    return books2;
                }
            }));
    System.out.println(books);
}

使用toMap()方法有一个注意事项,toMap()共有三种实现:

主要是前三个参数:

  • Function<? super T,? extends K> keyMapper:key的映射函数,将T类型映射为K类型
  • Function<? super T,? extends U> valueMapper:value的映射函数,将T类型映射为U类型
  • BinaryOperator mergeFunction:聚合函数,用于指定key重复时的操作

因为将流中元素的某个字段转化为key后是可能存在重复的,并且无法通过distinct()去重,distinct()只会去掉重复的对象,而不能仅对流中元素的某个字段去重。

BinaryOperator则是用来指导key重复时的聚合规则,实现BinaryOperator接口需要重写apply(o1, o2)方法,接收两个参数,如果 return o1 则表示保留先出现的key对应的value,return o2 则表示用后面出现的key对应的value覆盖前面的。

lambda表达式

1
2
3
4
5
6
7
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    Map<String, List<Book>> books = authors.stream()
            .collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks(), (books1, books2) -> books2));
    System.out.println(books);
}
查找和匹配
anyMatch

判断流内是否有任意符合匹配条件的元素,结果为boolean类型。只要有一个元素满足条件就返回true。

1
2
3
4
5
6
7
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    boolean b = authors.stream()
            .anyMatch(author -> author.getAge() > 29);
    System.out.println(b);  // true
}
allMatch

与anyMatch()类似,判断流内是否所有元素都满足匹配条件,结果为boolean类型。当所有元素都满足条件时才返回true。

noneMatch

与上面两个类似,判断流内是否所有元素都不满足匹配条件,结果为boolean类型。当所有元素都不满足条件时才返回true。

findAny

获取流中的任意一个元素,返回的是一个 Optional 对象。该方法没有办法保证获取的一定是流中的第一个元素,因此用的更多的是下面的 findFirst() 方法。

findAny() 并不像他的字面意思一样,可以查找一个满足条件的元素,他只是在最后处理完的流中随机获取一个元素并返回。因此,如果要做筛选的话,还是要依赖 filter() 方法。

那么 findAny() 和 findFirst() 存在的意义是什么呢?

因为流处理结束后,最终的流是可能为空的,比如说下面的代码中,如果作家年龄都小于18,那么最后的流将会是空的,如果直接使用很可能会报空指针异常。因此,findAny() 和 findFirst() 方法主要是用来避免空指针异常的。

当调用 findAny() 和 findFirst() 方法时,返回的是一个 Optional 对象,Optional 对象的 ifPresent() 方法便可以对流元素对象进行判空,不为空才执行相应逻辑。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    // 查找年龄大于18的作家
    Optional<Author> optionalAuthor = authors.stream()
            .filter(author -> author.getAge() > 18)
            .findAny();
    
    // 如果存在就输出他的名字
    optionalAuthor.ifPresent(author -> System.out.println(author));
}
findFirst

获取流中的第一个元素,返回的是一个 Optional 对象。与findAny()的用法一样。

reduce

对流中的数据按照指定的计算方式计算出 一个结果。

两个参数

1
2
3
4
5
6
7
8
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    Integer ageSum = authors.stream()
            .distinct()
            .map(Author::getAge)
            .reduce(0, (result, element) -> result + element);
}

一个参数

1
2
3
4
5
6
7
public static void main(String[] args) {
    List<Author> authors = getAuthors();

    Optional<Integer> reduce = authors.stream()
            .map(Author::getAge)
            .reduce((result, element) -> result > element ? result : element);
}

注意事项

惰性求值:在对流进行操作时,操作不会立即执行,而是等到需要结果时才进行计算,即没有结尾操作,中间操作是不会执行的。

流是一次性的:Stream 流是一次性的,一旦对流进行了结尾操作(如收集结果、循环遍历等),流就会被消耗掉,无法再次使用。

不会影响原数据

OPtional

概述

在从数据库查询数据或者执行一些其他操作的时候,查询出来的结果可能是为空的,返回的是 null,如果不对返回值进行判断,直接对 null 进行操作,则会报空指针异常。

传统的方式是使用 if 条件判断来判断对象是否为空

因此 Java 8 引入了 Optional 类,用于处理可能为空的值的容器类。它提供了一种优雅的方式来处理可能存在或不存在的值,避免了空指针异常的发生

使用

创建对象

Optional 就好像是包装类,可以把我们的具体数据封装到 Optional 对象内部。然后去使用 Optional 中封装好的方法操作封装进去的数据就可以避免空指针异常。

ofNullable

一般使用 Optional 的静态方法 ofNullable来把数据封装成一个 Optional 对象。如果传入的参数为null,将会返回一个空的 Optional 对象。

1
2
3
4
5
public static void main(String[] args) {
    Author author = getAuthor();
    Optional<Author> optionalAuthor = Optional.ofNullable(author);
    optionalAuthor.ifPresent(System.out::println);
}

这样处理感觉和使用 if 做判空好像差不多,在获取数据后都要加一行代码来处理。但是如果改造下 getAuthor 方法,让其的返回值就是封装好的 Optional 的话,我们在使用时就会方便很多。

而且在实际开发中,数据很多是从数据库获取的,MyBatis 从 3.5 版本开始也已经支持 Optional 了,可以直接把 Dao 层方法的返回值类型定义成 Optional 类型,封装的过程也不需要自己操作,MyBatis 会自己把数据封装成 Optional 对象返回。

of

of 方法只能处理非空的对象。如果确定一个对象不是空的,则可以使用 of 方法来把数据封装成 Optional 对象。

empty

返回一个空的 Optional 对象。如果明确知道某个对象为 null,那么可以直接使用 empty() 封装。

ofNullable() 本质也是调用 of() 和 empty() 实现的:

1
2
3
public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}

安全消费值

ifPresent

如果 Optional 对象中的对象不为 null,那么就会调用 consumer 中定义的逻辑处理,如果为 null,则不执行任何操作。

获取到一个 Optional 之后,可以使用其 ifPresent() 方法来消费其中的值。这个方法会判断其内封装的数据是否为空,不为空时才会执行具体的消费代码,这样使用起来就更加安全了。

1
2
3
4
5
public static void main(String[] args) {
    Author author = getAuthor();
    Optional<Author> optionalAuthor = Optional.ofNullable(author);
    optionalAuthor.ifPresent(System.out::println);
}

获取值

get

如果 Optional 中的值不为 null,则返回该对象,如果为空,则会抛 NoSuchElementException 异常。

如果想获取值自己进行处理,可以使用 get() 方法获取,但是因为空的 Optional 调用 get() 会抛异常,因此还需要使用ifPresent() 方法判断 Optional 中值是否存在,如果存在返回 true,否则返回 false。

1
2
3
4
5
6
7
8
public static void main(String[] args) {
    Author author = getAuthor();
    Optional<Author> optionalAuthor = Optional.of(author);
    if (optionalAuthor.isPresent()) {
        Author author2 = optionalAuthor.get();
        System.out.println(author2);
    }
}

安全获取值

orElseGet

如果存在则返回值,否则返回设置好的默认值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public static void main(String[] args) {
    Author author = getAuthor();
    Optional<Author> optionalAuthor = Optional.of(author);
    Author author2 = optionalAuthor.orElseGet(new Supplier<Author>() {
        @Override
        public Author get() {
            return new Author();
        }
    });
}

lambda

1
2
3
4
5
public static void main(String[] args) {
    Author author = getAuthor();
    Optional<Author> optionalAuthor = Optional.of(author);
    Author author2 = optionalAuthor.orElseGet(() -> new Author());
}

orElseThrow

如果存在则返回值,否则抛出 Supplier 指定的异常。

该方法在 Spring 框架中用的比较多,可以用 Spring 来做统一的异常捕获。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public static void main(String[] args) throws Throwable {
    Author author = getAuthor();
    Optional<Author> optionalAuthor = Optional.of(author);
    Author author2 = optionalAuthor.orElseThrow(new Supplier<Throwable>() {
        @Override
        public Throwable get() {
            return new RuntimeException();
        }
    });
}

lambda

1
2
3
4
5
public static void main(String[] args) throws Throwable {
    Author author = getAuthor();
    Optional<Author> optionalAuthor = Optional.of(author);
    Author author2 = optionalAuthor.orElseThrow(() -> new RuntimeException());
}

过滤

filter

如果存在值并且值满足设定的条件,返回满足条件的元素组成的 Optional,否则返回空的 Optional。

1
2
3
4
5
public static void main(String[] args) throws Throwable {
    Author author = getAuthor();
    Optional<Author> optionalAuthor = Optional.ofNullable(author);
    optionalAuthor.filter(author1 -> author1.getAge() > 18).ifPresent(author1 -> System.out.println(author1.getName()));
}

判断

ifPresent

对Optional对象中是否存在值进行判断,如果存在返回true,否则返回false。

一般都是直接使用 ifPresent(),单独使用 isPresent() 和使用 if(xx != null) 差不多。

数据转换

map

与 Stream 中的 map() 方法类似,用于数据转换或计算。

1
2
3
4
5
public static void main(String[] args) throws Throwable {
    Optional<Author> optionalAuthor = Optional.ofNullable(getAuthor());
    Optional<List<Book>> optionalBooks = optionalAuthor.map(author -> author.getBooks());
    optionalBooks.ifPresent(books -> books.forEach(book -> System.out.println(book.getName())));
}

函数式接口

函数式接口(Functional Interface)是指 只包含一个抽象方法 的接口。Java 8 引入了函数式接口的概念,以支持函数式编程的特性。

JDK 内置的函数式接口都加上了 @FunctionalInterface 注解,但是并非加这个注解的才是函数式接口,只要满足“只包含一个抽象方法”,就是函数式接口。

以下是函数式接口的一些特点和用途:

  • 单一抽象方法:函数式接口只能包含一个抽象方法,可以有默认方法和静态方法,但只能有一个抽象方法。这个抽象方法定义了接口的核心功能。
  • Lambda 表达式:函数式接口可以使用 Lambda 表达式来创建接口的实例。Lambda 表达式提供了一种简洁的方式来定义函数式接口的实现。
  • 方法引用:函数式接口可以使用方法引用来引用已有的方法作为接口的实现。方法引用提供了一种更简洁的方式来表示函数式接口的实现。
  • Java 8 Stream API:Java 8 的 Stream API 中广泛使用了函数式接口。Stream API 提供了一种流式操作的方式,可以对集合进行过滤、映射、排序等操作。

以Predicate为例,介绍一下函数式接口的用法

Predicate

and

在使用 Predicate 接口时,可能需要进行判断条件的拼接,而 and() 方法相当于使用 && 来拼接两个判断条件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public static void main(String[] args) {
    List<Author> authors = getAuthors();
    authors.stream()
            .filter(new Predicate<Author>() {
                @Override
                public boolean test(Author author) {
                    return author.getAge() > 17;
                }
            }.and(new Predicate<Author>() {
                @Override
                public boolean test(Author author) {
                    return author.getName().length() > 1;
                }
            }))
            .forEach(System.out::println);
}

lambda

1
2
3
4
5
6
public static void main(String[] args) {
    List<Author> authors = getAuthors();
    authors.stream()
            .filter(((Predicate<Author>) author -> author.getAge() > 17).and(author -> author.getName().length() > 1))
            .forEach(System.out::println);
}

or

相当于用 || 来拼接两个判断条件。

negate

Negate 相当于在判断条件前面加了个 ! 取反。

1
2
3
4
5
6
public static void main(String[] args) {
    List<Author> authors = getAuthors();
    authors.stream()
            .filter(((Predicate<Author>) author -> author.getAge() > 17).negate())
            .forEach(System.out::println);
}

方法引用

方法引用(Method Reference)是 Java 8 引入的一种语法糖,用于简化 Lambda 表达式的写法。它提供了一种更简洁的方式来引用已有的方法作为函数式接口的实现。

方法引用可以看作是 Lambda 表达式的一种特殊形式,如果方法体中只有一个方法的调用的话,它可以直接引用已有的方法,而不需要编写完整的 Lambda 表达式,进而进一步简化编码。

基本格式如下:

1
类名或者对象名::方法名

方法引用的使用取决于函数式接口的抽象方法的参数和返回类型。以下是方法引用的几种形式:

  • 静态方法引用:ClassName::staticMethodName,例如 Integer::parseInt
  • 实例方法引用:instance::instanceMethodName,例如 String::length
  • 对象方法引用:ClassName::instanceMethodName,例如 System.out::println
  • 构造方法引用:ClassName::new,例如 ArrayList::new

静态方法引用

如果在重写方法的时候,方法体中只有一行代码,并且这行代码是 调用了某个类的静态方法,并且我们把要重写的抽象方法中所有的参数都 按照顺序 传入了这个静态方法中,这时候就可以通过方法引用的方式调用该静态方法。

1
2
3
4
5
6
7
public static void main(String[] args) {
    List<Author> authors = getAuthors();
    authors.stream()
            .map(author -> author.getName())
            .map(name -> String.valueOf(name))
            .forEach(name -> System.out.println(name));
}

这里代码就可以用方法引用优化

1
2
3
4
5
6
7
public static void main(String[] args) {
    List<Author> authors = getAuthors();
    authors.stream()
            .map(author -> author.getName())  // 也可以使用方法引用,但是这里主要是介绍静态方法引用
            .map(String::valueOf)
            .forEach(System.out::println);
}

实例方法引用

如果在重写方法的时候,方法体中 只有一行代码,并且这行代码是 调用了某个对象的成员方法,并且把要重写的抽象方法中所有的参数都按照顺序传入了这个成员方法中,这个时候就可以引用对象的实例方法。

1
2
3
4
5
6
7
public static void main(String[] args) {
    List<Author> authors = getAuthors();
    authors.stream()
            .map(Author::getName)
            .map(String::valueOf)
            .forEach(System.out::println);
}
Licensed under 9u_l3
使用 Hugo 构建
主题 StackJimmy 设计