zoukankan      html  css  js  c++  java
  • 从java8 说起函数式编程

    写在前面

    为什么要用函数式编程。看例子:

    final List<BigDecimal> prices = Arrays.asList(
        new BigDecimal("10"), new BigDecimal("30"), new BigDecimal("17"),
        new BigDecimal("20"), new BigDecimal("15"), new BigDecimal("18"),
        new BigDecimal("45"), new BigDecimal("12"));
    
    BigDecimal totalOfDiscountedPrices = BigDecimal.ZERO;
    for(BigDecimal price : prices) {
        if(price.compareTo(BigDecimal.valueOf(20)) > 0)
            totalOfDiscountedPrices = totalOfDiscountedPrices.add(price.multiply(BigDecimal.valueOf(0.9)));
    }
    System.out.println("Total of discounted prices: " + totalOfDiscountedPrices);
    

    更优雅的方式,是使用声明式的代码:

    final BigDecimal totalOfDiscountedPrices = 
    	prices.stream()
    	      .filter(price -> price.compareTo(BigDecimal.valueOf(20)) > 0)
    	      .map(price -> price.multiply(BigDecimal.valueOf(0.9)))
    	      .reduce(BigDecimal.ZERO, BigDecimal::add);
    System.out.println("Total of discounted prices: " + totalOfDiscountedPrices);
    

    使用函数式代码的好处:

    • 减少了可变量(Immutable Variable)的声明
    • 能够更好的利用并行(Parallelism)
    • 代码更加简洁和可读

    函数式接口

    函数式接口就是仅声明了一个方法的接口,比如我们熟悉的Runnable,Callable,Comparable等都可以作为函数式接口。当然,在Java 8中,新添加了一类函数式接口,如Function,Predicate,Consumer,Supplier等。

    @FunctionalInterface
    public static interface IntHandler{
        void handle(int i);
    }
    

    在函数式接口中,可以声明0个或者多个default方法,这些方法在接口内就已经被实现了。因此,接口的default方法也是Java 8中引入的一个新概念。

    default method 默认方法,又称作虚拟扩展方法或保卫者方法,例子:

    import java.util.Objects;
    import java.util.function.Consumer;
    
    public interface Iterable<T> {
        default void forEach(Consumer<? super T> action) {
            Objects.requireNonNull(action);
            for (T t : this) {
                action.accept(t);
            }
        }
    }
    

    为什么需要default method?

    • 即使你的API已经发布出去了,你依然可以为接口添加新方法并且无需考虑向后兼容问题。
    • java8 对Lambda的支持必然会影响JDK API的接口,如果直接在接口中添加方法,就会导致所有实现该接口的类或者接口无法通过编译。

    函数式接口使用 @FunctionalInterface注解进行标注。虽然这个注解的使用不是强制性的,但是使用它的好处是让此接口的目的更加明确,同时编译器也会对代码进行检查,来确保被该注解标注的接口的使用没有语法错误。

    lambda表达式

    lambda表达式即匿名函数,它是一段没有函数名的函数体,可以作为参数直接传递给相关调用者。

    Lambda 表达式通常使用 (argument) -> (body) 语法书写,例如:

    (arg1, arg2...) -> { body }
    (type1 arg1, type2 arg2...) -> { body }
    

    举例来说:

    (int a, int b) -> {  return a + b; }
    () -> System.out.println("Hello World");
    (String s) -> { System.out.println(s); }
    () -> 42
    () -> { return 3.1415 };
    

    Lambda 表达式的结构

    1. 一个 Lambda 表达式可以有零个或多个参数
    2. 参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同
    3. 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a, b) 或 (int a, int b) 或 (String a, int b, float c)
    4. 空圆括号代表参数集为空。例如:() -> 42
    5. 当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> return a*a
    6. Lambda 表达式的主体可包含零条或多条语句
    7. 如果 Lambda 表达式的主体只有一条语句,花括号{}可省略。匿名函数的返回类型与该主体表达式一致
    8. 如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空

    每个 Lambda 表达式都能隐式地赋值给函数式接口,例如,我们可以通过 Lambda 表达式创建 Runnable 接口的引用。

    Runnable r = () -> System.out.println("hello world");
    

    方法引用

    方法引用是Java 8中提出的用来简化lambda表达式的一种手段。它通过类名和方法名来定位到一个静态方法或者实例方法。 方法引用在Java 8中的使用非常灵活。总的来说,可以分为以下几种:

    1. 静态方法引用:ClassName::methodName

       List<Integer> ints = Arrays.asList(1, 2, 3);
       ints.sort(Integer::compare);
      
    2. 实例上的实例方法引用 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);
      

    Lambda 表达式举例

    1.线程初始化

    //旧方法:
    new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello from thread");
    }
    }).start();
    
    //新方法:
    new Thread(
    () -> System.out.println("Hello from thread")
    ).start();
    

    2.打印数组所有元素,在下面的例子中,我们先是用常用的箭头语法创建 Lambda 表达式,之后,使用 Java 8 全新的双冒号(::)操作符将一个常规方法转化为 Lambda 表达式:

    //Old way:
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    for(Integer n: list) {
       System.out.println(n);
    }
    
    //New way:
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    list.forEach(n -> System.out.println(n));
    
    
    //or we can use :: double colon operator in Java 8
    list.forEach(System.out::println);
    

    3.断言函数式接口使用

    import java.util.Arrays;
    import java.util.List;
    import java.util.function.Predicate;
    
    public class Test {
    	public static void main(String[] args) {
    		List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    		System.out.println("Print all numbers:");
    		evaluate(list, n -> true);
    
    		System.out.println("Print no numbers:");
    		evaluate(list, n -> false);
    
    		System.out.println("Print even numbers:");
    		evaluate(list, n -> n % 2 == 0);
    
    		System.out.println("Print odd  numbers:");
    		evaluate(list, n -> n % 2 == 1);
    
    		System.out.println("Print numbers greater than 5:");
    		evaluate(list, n -> n > 5);
    	}
    
    	public static void evaluate(List<Integer> list, Predicate<Integer> predicate) {
    		for (Integer n : list) {
    			if (predicate.test(n))
    				System.out.print(n + " ");
    		}
    	}
    }
    

    Lambda 表达式与匿名类的区别

    使用匿名类与 Lambda 表达式的一大区别在于关键词的使用。对于匿名类,关键词 this 解读为匿名类,而对于 Lambda 表达式,关键词 this 解读为写就 Lambda 的外部类。

    Lambda 表达式与匿名类的另一不同在于两者的编译方法。Java 编译器编译 Lambda 表达式并将他们转化为类里面的私有函数,它使用 Java 7 中新加的 invokedynamic 指令动态绑定该方法

    接口与抽象类

    虽然 Java 8 的接口的默认方法就像抽象类,能提供方法的实现,但是他们俩仍然是 不可相互代替的:

    • 接口可以被类多实现(被其他接口多继承),抽象类只能被单继承。
    • 接口中没有 this 指针,没有构造函数,不能拥有实例字段(实例变量)或实例方法,无法保存 状态(state),抽象方法中可以。
    • 抽象类不能在 java 8 的 lambda 表达式中使用。
    • 从设计理念上,接口反映的是 “like-a” 关系,抽象类反映的是 “is-a” 关系。
  • 相关阅读:
    选择排序
    冒泡排序
    java多线程
    Java中的内存泄露的几种可能
    "==" 与 “equals”
    保证service不被杀死的方法
    反射、注解、依赖
    引导页
    适配:px与dp转换
    四 主要的几种 Web 服务器
  • 原文地址:https://www.cnblogs.com/tina-smile/p/5756074.html
Copyright © 2011-2022 走看看