函数式编程语言操纵代码片段就像操作数据一样容易。 虽然 Java 不是函数式语言,但 Java 8 Lambda 表达式和方法引用 (Method References) 允许你以函数式编程。
新旧对比
通过下面的案例引入匿名内部类, lambda表达式, 方法引用, 作出对比
// functional/Strategize.java
interface Strategy { String approach(String msg); } class Soft implements Strategy { public String approach(String msg) { return msg.toLowerCase() + "?"; } } class Unrelated { static String twice(String msg) { return msg + " " + msg; } } public class Strategize { Strategy strategy; String msg; Strategize(String msg) { strategy = new Soft(); // [1] 在 Strategize 中,Soft 作为默认策略,在构造函数中赋值。 this.msg = msg; } void communicate() { System.out.println(strategy.approach(msg)); } void changeStrategy(Strategy strategy) { this.strategy = strategy; } public static void main(String[] args) { Strategy[] strategies = { new Strategy() { // [2]创建一个匿名内部类,代码显得比较冗余 public String approach(String msg) { return msg.toUpperCase() + "!"; } }, msg -> msg.substring(0, 5), // [3]Java8的 Lambda表达式。由箭头 -> 分隔开参数和函数体, 箭头左边是参数,箭头右侧是从Lambda返回的表达式,即函数体。 这实现了与定义类、匿名内部类相同的效果,但代码少得多。 Unrelated::twice // [4]Java 8 的方法引用,由 :: 区分。 在 :: 的左边是类或对象的名称,在 :: 的右边是方法的名称,但没有参数列表 }; Strategize s = new Strategize("Hello there"); s.communicate(); for(Strategy newStrategy : strategies) { s.changeStrategy(newStrategy); // [5] 在使用默认的 Soft strategy 之后, 我们逐步遍历数组中的所有 Strategy, 并使用changeStrategy()方法将每个Strategy放入变量s中 s.communicate(); // [6]每次调用 communicate() 都会产生不同的行为, 具体取决于此刻正在使用的策略代码对象。我们传递的是行为,而非仅数据 } } }
output:
hello there? HELLO THERE! Hello Hello there Hello there
Lambda表达式
带有参数变量的表达式,是一段可以传递的代码,可以被一次或多次执行
是一种精简的字面写法,其实就是把匿名内部类中“一定”要做的工作省略掉,然后由JVM通过推导把简化的表达式还原
格式: (parameters参数) -> expression表达式或方法体
paramaters:
类似方法中的形参列表,这里的参数是函数式接口里的参数
-> :
可理解为“被用于”的意思
方法体:
可以是表达式也可以代码块,是函数式接口里方法的实现
如果负责运算的代码无法用表达式表示,可以使用编写方法实现
但必须用{}包围并按需明确使用 return语句
// functional/LambdaExpressions.java
interface Description { String brief(); } interface Body { String detailed(String head); } interface Multi { String twoArg(String head, Double d); } public class LambdaExpressions { //当只用一个参数,可以不需要括号 () static Body bod = h -> h + " No Parens!"; // [1] //正常情况使用括号 () 包裹参数, //为了保持一致性,也可以使用括号 ()包裹单个参数,虽然这种情况并不常见。 static Body bod2 = (h) -> h + " More details"; // [2]。 //如果没有参数,则必须使用括号 () 表示空参数列表 static Description desc = () -> "Short info"; // [3] //对于多个参数,将参数列表放在括号 () 中 static Multi mult = (h, n) -> h + n; // [4] /**到目前为止,所有 Lambda 表达式方法体都是单行。 该表达式的结果自动成为 Lambda 表达式的返回值,在此处使用 return 关键字是非法的。 这是 Lambda 表达式缩写用于描述功能的语法的另一种方式。 */ //如果在 Lambda 表达式中确实需要多行,则必须将这些行放在花括号中。 //在这种情况下,就需要使用 return。 static Description moreLines = () -> { // [5] System.out.println("moreLines()"); return "from moreLines()"; }; public static void main(String[] args) { System.out.println(bod.detailed("Oh!")); System.out.println(bod2.detailed("Hi!")); System.out.println(desc.brief()); System.out.println(mult.twoArg("Pi! ", 3.14159)); System.out.println(moreLines.brief()); } }
output:
Oh! No Parens! Hi! More details Short info Pi! 3.14159 moreLines() from moreLines()