重识Java8函数式编程

最近真的是太忙忙忙忙忙了,很久没有更新文章了。最近工作中看到了几段关于函数式编程的代码,但是有点费解,于是就准备总结一下函数式编程。很多东西很简单,但是如果不总结,可能会被它的各种变体所困扰。接触Lambda表达式已经很久了,但是也一直是处于照葫芦画瓢的阶段,所以想自己去编写相关代码,也有些捉襟见肘。

1. Lambda表达式的不同形式 // 基本形式 参数 -> 主体 1.1 形式一 Runnable noArguments = () -> System.out.println("Hello World");

该形式的Lambda表达式不包含参数,使用空括号()表示没有参数。它实现了Runnable接口,该接口也只有一个run方法,没有桉树,且返回类型为void。

1.2 形式二 ActionListener oneArgument = event -> System.out.println("button clicked");

该形式的Lambda表达式包含且只包含一个参数,可省略参数的符号。

1.3 形式三 Runnable multiStatement = () -> { System.out.print("Hello"); System.out.println(" World"); };

Lambda表达式的主体不仅可以使一个表达式,而且也可以是一段代码块,使用大括号{}将代码块括起来。该代码块和普通方法遵循的规则别无二致,可以用返回或抛出异常来退出。只有以行代码的Lambda表达式也可以使用大括号,用以明确Lambda表达式从何处开始,到哪里结束。

1.4 形式四 BinaryOperator<Long> add = (x, y) -> x + y;

Lambda表达式也可以表示包含多个参数的方法,上面的Lambda表达式并不是将两个数字相加,而是创建了一个函数,用来计算两个数字相加的结果。变量add的类型时BinaryOperator,它不是两个数字的和,而是将两个数字相加的那行代码。

1.5 形式五 BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y;

到目前为止,所有Lambda表达式中的参数类型都是由编译器推断得出的。但有时最好也可以显示声明参数类型,此时就需要使用小括号将参数括起来,多个参数的情况也是如此。

2. 引用值,而不是变量

如果你曾使用过匿名内部类,也许遇到过这样的情况:需要引用它所在方法里的变量。这是,需要将变量声明为final。

final String name = getUserName(); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.out.println("hi " + name); } });

将变量声明为 final,意味着不能为其重复赋 值。同时也意味着在使用 final 变量时,实际上是在使用赋给该变量的一个特定的值。

Java 8 虽然放松了这一限制,可以引用非 final 变量,但是该变量在既成事实上必须是 final(意思就是你不能再次对该变量赋值)。虽然无需将变量声明为 final,但在 Lambda 表达式中,也无法用作非终态变量。如 果坚持用作非终态变量,编译器就会报错。 既成事实上的 final 是指只能给该变量赋值一次。换句话说,Lambda 表达式引用的是值, 而不是变量。

例如:

String name = getUserName(); button.addActionListener(event -> System.out.println("hi " + name)); 3. 函数接口

在 Java 里,所有方法参数都有固定的类型。假设将数字 3 作为参数传给一个方法,则参数 的类型是 int。那么,Lambda 表达式的类型又是什么呢?

使用只有一个方法的接口来表示某特定方法并反复使用,是很早就有的习惯。使用 Swing 编写过用户界面的人对这种方式都不陌生,这里无需再标新立异,Lambda 表达式也使用同样的技巧,并将这种接口称为函数接口。

接口中单一方法的命名并不重要,只要方法签名和 Lambda 表达式的类型匹配即可。可在函数接口中为参数起一个有意义的名字,增加代码易读性,便于更透彻 地理解参数的用途。

3.1 Java中重要的函数接口 接口 参数 返回类型 示例
Predicate   T   boolean   判断是否  
Consumer   T   void   输出一个值  
Function<T,R>   T   T   获得对象的名字  
Supplier   None   T   工厂方法  
UnaryOperator   T   T   逻辑非(!)  
BinaryOperator   (T, T)   T   求两个数的乘积(*)  
3.2 函数接口定义

定义函数接口需要使用到注解@FunctionalInterface

例如:

@FunctionalInterface public interface MyFuncInterface { void print(); }

使用:

public class MyFunctionalInterfaceTest { public static void main(String[] args) { doPrint(() -> System.out.println("java")); } public static void doPrint(MyFuncInterface my) { System.out.println("请问你喜欢什么编程语言?"); my.print(); } }

说明:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsxdxs.html