# JDK8 函数式编程

# lambda表达式

  • lambda表达式即匿名函数,它是一段没有函数名的函数体,可以作为参数直接传递给相关调用者.
  • Lambda 表达式通常使用 (argument) -> (body) 语法书写:
//有参数,且有返回值
(int a, int b) -> {  return a + b; }
//无参无返回值
() -> System.out.println("Hello World");
//有参无返回值
(String s) -> { System.out.println(s); }
// 无参有返回值
() -> { return 3.1415 };
  • 使用,例:
// 定义一个函数式接口
@FunctionalInterface
public interface MyMap{
    int getKV(int k, int v);
}

// 调用
public static void main(String[] args) {
    MyMap myMap = (int a,int b)-> a + b;
    int kv = myMap.getKV(5, 6);
    System.out.println(kv);
}
  • Lambda 表达式的结构:

    • 一个 Lambda 表达式可以有零个或多个参数。
    • 参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同。
    • 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a, b) 或 (int a, int b) 或 (String a, int b, float c)
    • 空圆括号代表参数集为空。例如:() -> 42
    • 当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> return a*a
    • Lambda 表达式的主体可包含零条或多条语句。
    • 如果 Lambda 表达式的主体只有一条语句,花括号{}可省略。匿名函数的返回类型与该主体表达式一致。
    • 如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空。
  • 方法引用:是Java 8中提出的用来简化lambda表达式的一种手段。它通过类名和方法名来定位到一个静态方法或者实例方法。 方法引用在Java 8中的使用非常灵活。总的来说,可以分为以下几种:

    • 静态方法引用:ClassName::methodName
    List<Integer> ints = Arrays.asList(1, 2, 3);
    ints.sort(Integer::compare);
    
    • 实例上的实例方法引用:instanceReference::methodName 方法引用使用”::”定义,”::”的前半部分表示类名或者实例名,后半部分表示方法名称。如果是构造函数,则使用new表示。
    words.forEach(System.out::println);
    
    words.stream().map(word -> word.length()); // lambda
    words.stream().map(String::length); // method reference
    
    // lambda
    words.stream().map(word -> {
        return new StringBuilder(word);
    });
    // constructor reference
    words.stream().map(StringBuilder::new);
    

# 函数式接口

TIP

函数式接口:它指的是有且只有一个未实现的方法的接口,一般通过@FunctionalInterface这个注解来表明某个接口是一个函数式接口。函数式接口是Java支持函数式编程的基础。

  • Consumer<T>
- T:入参类型;没有出参
- 调用方法:void accept(T t);
- 定义函数示例:Consumer<String> consumer= p -> System.out.println(p);
- 调用函数示例:consumer.accept("a b c");
  • Function<T, R>
- T: 入参类型;R:出参类型
- 调用方法:R apply(T t); 
- 定义函数示例:Function<Integer, Integer> func = p -> p * 10; 
- 调用函数示例:func.apply(10); 
  • Predicate<T>
- T:入参类型;出参类型是Boolean
- 调用方法:boolean test(T t);
- 定义函数示例:Predicate<Integer> predicate = p -> p % 2 == 0;  
- 调用函数示例:predicate.test(100);
  • Supplier<T>
- T:出参类型;没有入参
- 调用方法:T get();
- 定义函数示例:Supplier<Integer> supplier= () -> 100;  
- 调用函数示例:supplier.get();

# 函数式编程接口的使用

# Stream

TIP

Stream可以对多个元素进行一系列的操作,也可以支持对某些操作进行并发处理。

  • Stream对象的创建:
//a: 创建空的Stream对象
Stream stream = Stream.empty();
//b: 通过集合类中的stream或者parallelStream方法创建
List<String> list = Arrays.asList("a", "b", "c", "d");
Stream listStream = list.stream();                   //获取串行的Stream对象
Stream parallelListStream = list.parallelStream();   //获取并行的Stream对象  
//c:通过Stream中的of方法创建
Stream s = Stream.of("test");
Stream s1 = Stream.of("a", "b", "c", "d");
//d:通过Stream中的iterate方法创建
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f);  
public static<T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)
  • Stream对象的使用 Stream对象提供多个非常有用的方法,这些方法可以分成两类:
    • 中间操作:将原始的Stream转换成另外一个Stream;如filter返回的是过滤后的Stream。
    • 终端操作:产生的是一个结果或者其它的复合操作;如count或者forEach操作。

所有中间操作

方法 说明
sequential 返回一个相等的串行的Stream对象,如果原Stream对象已经是串行就可能会返回原对象
parallel 返回一个相等的并行的Stream对象,如果原Stream对象已经是并行的就会返回原对象
unordered 返回一个不关心顺序的Stream对象,如果原对象已经是这类型的对象就会返回原对象
onClose 返回一个相等的Steam对象,同时新的Stream对象在执行Close方法时会调用传入的Runnable对象
close 关闭Stream对象
filter 元素过滤:对Stream对象按指定的Predicate进行过滤,返回的Stream对象中仅包含未被过滤的元素
map 元素一对一转换:使用传入的Function对象对Stream中的所有元素进行处理,返回的Stream对象中的元素为原元素处理后的结果
mapToInt 元素一对一转换:将原Stream中的使用传入的IntFunction加工后返回一个IntStream对象
flatMap 元素一对多转换:对原Stream中的所有元素进行操作,每个元素会有一个或者多个结果,然后将返回的所有元素组合成一个统一的Stream并返回
distinct 去重:返回一个去重后的Stream对象
sorted 排序:返回排序后的Stream对象
peek 使用传入的Consumer对象对所有元素进行消费后,返回一个新的包含所有原来元素的Stream对象
limit 获取有限个元素组成新的Stream对象返回
skip 抛弃前指定个元素后使用剩下的元素组成新的Stream返回

所有终端操作

方法 说明
iterator 返回Stream中所有对象的迭代器
spliterator 返回对所有对象进行的spliterator对象
forEach 对所有元素进行迭代处理,无返回值
forEachOrdered 按Stream的Encounter所决定的序列进行迭代处理,无返回值
toArray 返回所有元素的数组
reduce 使用一个初始化的值,与Stream中的元素一一做传入的二合运算后返回最终的值。每与一个元素做运算后的结果,再与下一个元素做运算。它不保证会按序列执行整个过程。
collect 根据传入参数做相关汇聚计算
min 返回所有元素中最小值的Optional对象;如果Stream中无任何元素,那么返回的Optional对象为Empty
max 与Min相反
count 所有元素个数
anyMatch 只要其中有一个元素满足传入的Predicate时返回True,否则返回False
allMatch 所有元素均满足传入的Predicate时返回True,否则False
noneMatch 所有元素均不满足传入的Predicate时返回True,否则False
findFirst 返回第一个元素的Optioanl对象;如果无元素返回的是空的Optional; 如果Stream是无序的,那么任何元素都可能被返回
findAny 返回任意一个元素的Optional对象,如果无元素返回的是空的Optioanl。
isParallel 判断是否当前Stream对象是并行的
  • 常用的方法举例:
    • filter
    //方法定义
    Stream<T> filter(Predicate<? super T> predicate);
    
    //使用示例:
    Stream<String> s = Stream.of("test", "t1", "t2", "teeeee", "aaaa");
    //查找所有包含t的元素并进行打印
    s.filter(n -> n.contains("t")).forEach(System.out::println);
    
    • map
    //方法定义
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    
    //使用示例:
    Stream<String> s = Stream.of("test", "t1", "t2", "teeeee", "aaaa");
    s.map(n -> n.concat(".txt")).forEach(System.out::println);
    
    • flatMap
    //方法定义
    <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
    
    //使用示例:
    Stream<String> s = Stream.of("test", "t1", "t2", "teeeee", "aaaa");
    s.flatMap(n -> Stream.of(n.split(""))).forEach(System.out::println);
    

# Optional

TIP

用于简化Java中对空值的判断处理,以防止出现各种空指针异常。 Optional实际上是对一个变量进行封装,它包含有一个属性value,实际上就是这个变量的值。

  • Optional对象创建:
//empty用于创建一个空的Optional对象;其value属性为Null。
Optional o = Optional.empty();
//of根据传入的值构建一个Optional对象; 传入的值必须是非空值,
//否则如果传入的值为空值,则会抛出空指针异常。
Optional o = Optional.of("test");
//ofNullable根据传入值构建一个Optional对象,传入的值可以是空值,
//如果传入的值是空值,则与empty返回的结果是一样的
Optional o = Optional.ofNullable(null);
  • Optional方法
方法名 说明
get 获取Value的值,如果Value值是空值,则会抛出NoSuchElementException异常;因此返回的Value值无需再做空值判断,只要没有抛出异常,都会是非空值。
isPresent Value是否为空值的判断;
ifPresent 当Value不为空时,执行传入的Consumer;
ifPresentOrElse Value不为空时,执行传入的Consumer;否则执行传入的Runnable对象;
filter 当Value为空或者传入的Predicate对象调用test(value)返回False时,返回Empty对象;否则返回当前的Optional对象
map 一对一转换:当Value为空时返回Empty对象,否则返回传入的Function执行apply(value)后的结果组装的Optional对象;
flatMap 一对多转换:当Value为空时返回Empty对象,否则传入的Function执行apply(value)后返回的结果(其返回结果直接是Optional对象)
or 如果Value不为空,则返回当前的Optional对象;否则,返回传入的Supplier生成的Optional对象;
stream 如果Value为空,返回Stream对象的Empty值;否则返回Stream.of(value)的Stream对象;
orElse Value不为空则返回Value,否则返回传入的值;
orElseGet Value不为空则返回Value,否则返回传入的Supplier生成的值;
orElseThrow Value不为空则返回Value,否则抛出Supplier中生成的异常对象;
  • Optional使用场景

    • 判断结果不为空后使用
    public class Demo {
     //获取字符串
     public static String getStr() {
         Random random = new Random();
         int i = random.nextInt(5);
         return i % 2 == 0 ? "not null" : null;
     }
     //打印string
     public static void sy(String n) {
         System.out.println(n);
     }
     //判断getStr()不为null,执行sy()方法
     public static void main(String[] args) {
         Optional o1 = Optional.ofNullable(getStr());
         o1.ifPresent(n -> sy((String) n));
    
      }
    }
    
    • 变量为空时提供默认值
    public class Demo {
    
    public static String getStr() {
        Random random = new Random();
        int i = random.nextInt(5);
        return i % 2 == 0 ? "not null" : null;
    }
    
    //为空时默认值为'is null'
    public static void main(String[] args) {
        Optional o1 = Optional.ofNullable(getStr());
        System.out.println(o1.orElse("is null"));
      }
    }
    
    • 变量为空时抛出异常,否则使用
    public class Demo {
    
    public static String getStr() {
        Random random = new Random();
        int i = random.nextInt(5);
        return i % 2 == 0 ? "not null" : null;
    }
    
    
    public static void main(String[] args) throws Throwable {
          Optional o1 = Optional.ofNullable(getStr());
          System.out.println(o1.orElseThrow(() -> new AppException("test")));
        }
    }