为了让代码更加一致和容易维护,编写大量代码,从易于理解、充分测试及可靠的现有小块开始,最后将它们组合在一起以创建新代码。
这就是函数式编程(FP)的意义所在。通过合并现有代码来生成新功能而不是从头开始编写所有内容,我们可以更快地获得更可靠的代码。至少在某些情况下,这套理论似乎很有用。在这一过程中,一些非函数式语言已经习惯了使用函数式编程产生的优雅的语法。
纯粹的函数式语言在安全性方面更进一步。它强加了额外的约束,即所有数据必须是不可变的:设置一次,永不改变。将值传递给函数,该函数然后生成新值但从不修改自身外部的任何东西(包括其参数或该函数范围之外的元素)。当强制执行此操作时,你知道任何错误都不是由所谓的副作用引起的,因为该函数仅创建并返回结果,而不是其他任何错误。
package xx;
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();
this.msg = msg;
}
void communicate() {
System.out.println(strategy.approach(msg));
}
void changeStrategy(Strategy strategy) {
this.strategy = strategy;
}
public static void main(String[] args) {
// interface 实现
Strategy[] strategies = {
// 一种略显冗长且更自发的方法是创建一个`匿名内部类`
new Strategy() {
public String approach(String msg) {
return msg.toUpperCase() + "!";
}
},
// Java 8 的 Lambda 表达式。由箭头 `->` 分隔开参数和函数体,箭头左边是参数,箭头右侧是从 Lambda 返回的表达式,即函数体。这实现了与定义类、匿名内部类相同的效果,但代码少得多。
msg -> msg.substring(0, 5),
// Java 8 的**方法引用**,由 `::` 区分。在 `::` 的左边是类或对象的名称,在 `::` 的右边是方法的名称,但没有参数列表。
Unrelated::twice
};
Strategize s = new Strategize("Hello there");
s.communicate();
// 逐步遍历数组中的所有 **Strategy**,并使用 `changeStrategy()` 方法将每个 **Strategy** 放入 变量 `s` 中。
for (Strategy newStrategy : strategies) {
s.changeStrategy(newStrategy);
s.communicate();
}
}
}
package xx;
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();
this.msg = msg;
}
void communicate() {
System.out.println(strategy.approach(msg));
}
void changeStrategy(Strategy strategy) {
this.strategy = strategy;
}
public static void main(String[] args) {
// interface 实现
Strategy[] strategies = {
// 一种略显冗长且更自发的方法是创建一个`匿名内部类`
new Strategy() {
public String approach(String msg) {
return msg.toUpperCase() + "!";
}
},
// Java 8 的 Lambda 表达式。由箭头 `->` 分隔开参数和函数体,箭头左边是参数,箭头右侧是从 Lambda 返回的表达式,即函数体。这实现了与定义类、匿名内部类相同的效果,但代码少得多。
msg -> msg.substring(0, 5),
// Java 8 的**方法引用**,由 `::` 区分。在 `::` 的左边是类或对象的名称,在 `::` 的右边是方法的名称,但没有参数列表。
Unrelated::twice
};
Strategize s = new Strategize("Hello there");
s.communicate();
// 逐步遍历数组中的所有 **Strategy**,并使用 `changeStrategy()` 方法将每个 **Strategy** 放入 变量 `s` 中。
for (Strategy newStrategy : strategies) {
s.changeStrategy(newStrategy);
s.communicate();
}
}
}
Lambda 表达式
Lambda 表达式是使用最小可能语法编写的函数定义:
- Lambda 表达式产生函数,而不是类。
在 JVM(Java Virtual Machine,Java 虚拟机)上,一切都是一个类
,因此在幕后执行各种操作使 Lambda 看起来像函数 —— 但作为程序员,你可以高兴地假装它们 “只是函数”。 - Lambda 语法尽可能多,这正是为了使 Lambda 易于编写和使用。
package xx;
interface Description {
String brief();
}
interface Body {
String detailed(String head);
}
interface Multi {
String twoArg(String head, Double d);
}
public class LambdaExpressions {
// 生成方法:
// 协议 协议名字 = lambda
static Body bod = h -> h + " No Parens!";
static Body bod2 = (h) -> h + " More details";
static Description desc = () -> "Short info";
static Multi mult = (h, n) -> h + n;
static Description moreLines = () -> {
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());
}
}
package xx;
interface Description {
String brief();
}
interface Body {
String detailed(String head);
}
interface Multi {
String twoArg(String head, Double d);
}
public class LambdaExpressions {
// 生成方法:
// 协议 协议名字 = lambda
static Body bod = h -> h + " No Parens!";
static Body bod2 = (h) -> h + " More details";
static Description desc = () -> "Short info";
static Multi mult = (h, n) -> h + n;
static Description moreLines = () -> {
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());
}
}
方法引用
Java 8 方法引用没有历史包袱。方法引用组成:类名或对象名,后面跟 ::
,然后跟方法名称。
class Describe {
void show(String msg) {
System.out.println(msg);
}
}
interface Callable {
void call(String s);
}
public class LambdaExpressions {
public static void main(String[] args) {
Describe d = new Describe();
Callable c = d::show;
c.call("call()");
}
}
class Describe {
void show(String msg) {
System.out.println(msg);
}
}
interface Callable {
void call(String s);
}
public class LambdaExpressions {
public static void main(String[] args) {
Describe d = new Describe();
Callable c = d::show;
c.call("call()");
}
}
构造函数引用
::new
class Dog {
String name;
int age = -1;
Dog() { name = "stray"; }
Dog(String nm) { name = nm; }
Dog(String nm, int yrs) { name = nm; age = yrs; }
}
interface MakeNoArgs {
Dog make();
}
interface Make1Arg {
Dog make(String nm);
}
interface Make2Args {
Dog make(String nm, int age);
}
public class CtorReference {
public static void main(String[] args) {
MakeNoArgs mna = Dog::new;
Make1Arg m1a = Dog::new;
Make2Args m2a = Dog::new;
Dog dn = mna.make();
Dog d1 = m1a.make("Comet");
Dog d2 = m2a.make("Ralph", 4);
}
}
class Dog {
String name;
int age = -1;
Dog() { name = "stray"; }
Dog(String nm) { name = nm; }
Dog(String nm, int yrs) { name = nm; age = yrs; }
}
interface MakeNoArgs {
Dog make();
}
interface Make1Arg {
Dog make(String nm);
}
interface Make2Args {
Dog make(String nm, int age);
}
public class CtorReference {
public static void main(String[] args) {
MakeNoArgs mna = Dog::new;
Make1Arg m1a = Dog::new;
Make2Args m2a = Dog::new;
Dog dn = mna.make();
Dog d1 = m1a.make("Comet");
Dog d2 = m2a.make("Ralph", 4);
}
}
函数式接口
函数式接口
- 当然首先是一个接口
- 然后就是在这个接口里面只能有一个抽象方法
- 可包含默认方法 defaul xxx
- 可包含允许定义静态方法
- 可定义java.lang.Object里的public方法
Java 8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。 提醒编译器去检查该接口是否仅包含一个抽象方法。
@FunctionalInterface
interface GreetingService {
// 只能一个抽象方法
void sayMessage(String message);
// 可用定义静态方法
static void printHello(){
System.out.println("Hello");
}
// 可定义默认方法
default void doSomeMoreWork1(){
// Method body
}
// 可定义java.lang.Object里的public方法
@Override
boolean equals(Object obj);
}
@FunctionalInterface
interface GreetingService {
// 只能一个抽象方法
void sayMessage(String message);
// 可用定义静态方法
static void printHello(){
System.out.println("Hello");
}
// 可定义默认方法
default void doSomeMoreWork1(){
// Method body
}
// 可定义java.lang.Object里的public方法
@Override
boolean equals(Object obj);
}
使用举例
@FunctionalInterface
interface Functional {
String goodbye(String arg);
}
interface FunctionalNoAnn {
String goodbye(String arg);
}
public class LamdaEx {
public String goodbye(String arg) {
return "Goodbye, " + arg;
}
public static void main(String[] args) {
LamdaEx fa = new LamdaEx();
Functional f = fa::goodbye;
FunctionalNoAnn fna = fa::goodbye;
Functional fl = a -> "Goodbye, " + a;
FunctionalNoAnn fnal = a -> "Goodbye, " + a;
}
}
@FunctionalInterface
interface Functional {
String goodbye(String arg);
}
interface FunctionalNoAnn {
String goodbye(String arg);
}
public class LamdaEx {
public String goodbye(String arg) {
return "Goodbye, " + arg;
}
public static void main(String[] args) {
LamdaEx fa = new LamdaEx();
Functional f = fa::goodbye;
FunctionalNoAnn fna = fa::goodbye;
Functional fl = a -> "Goodbye, " + a;
FunctionalNoAnn fnal = a -> "Goodbye, " + a;
}
}
个人理解是为lamda参数的类型推断提供了依据。@FunctionalInterface 限制了不可能出现重载方法?
多参数函数式接口
java.util.functional
中的接口是有限的。比如有了 BiFunction
,但它不能变化。 如果需要 三参数函数 的接口怎么办? 其实这些接口非常简单,很容易查看 Java 库源代码并自行创建。代码示例:
@FunctionalInterface
public interface TriFunction<T, U, V, R> {
R apply(T t, U u, V v);
}
简单测试,验证它是否有效:
public class TriFunctionTest {
static int f(int i, long l, double d) { return 99; }
public static void main(String[] args) {
TriFunction<Integer, Long, Double, Integer> tf =
TriFunctionTest::f;
tf = (i, l, d) -> 12;
}
}
这里我们测试方法引用和 Lambda 表达式。
高阶函数
这个名字听起来有点令人生畏,但是:高阶函数(Higher-order Function)只是一个消费或产生函数的函
import java.util.function.*;
interface FuncSS extends Function<String, String> {
}
public class ProduceFunction {
static FuncSS produce() {
return s -> s.toLowerCase();
}
public static void main(String[] args) {
FuncSS f = produce();
System.out.println(f.apply("YELLING"));
FuncSS f1 = (item) -> item + " Good";
System.out.println(f1.apply("Hell"));
}
}
import java.util.function.*;
interface FuncSS extends Function<String, String> {
}
public class ProduceFunction {
static FuncSS produce() {
return s -> s.toLowerCase();
}
public static void main(String[] args) {
FuncSS f = produce();
System.out.println(f.apply("YELLING"));
FuncSS f1 = (item) -> item + " Good";
System.out.println(f1.apply("Hell"));
}
}
要消费一个函数,消费函数需要在参数列表正确地描述函数类型。代码示例:
import java.util.function.*;
class One {}
class Two {}
public class ConsumeFunction {
static Two consume(Function<One,Two> onetwo) {
return onetwo.apply(new One());
}
public static void main(String[] args) {
Two two = consume(one -> new Two());
}
}
import java.util.function.*;
class One {}
class Two {}
public class ConsumeFunction {
static Two consume(Function<One,Two> onetwo) {
return onetwo.apply(new One());
}
public static void main(String[] args) {
Two two = consume(one -> new Two());
}
}
- andThen
- 返回一个先执行当前函数对象apply方法再执行after函数对象apply方法的函数对象。
- compose
- 返回一个先执行before函数对象apply方法再执行当前函数对象apply方法的函数对象
//返回一个先执行当前函数对象apply方法再执行after函数对象apply方法的函数对象。
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
//返回一个先执行before函数对象apply方法再执行当前函数对象apply方法的函数对象
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
//返回一个先执行当前函数对象apply方法再执行after函数对象apply方法的函数对象。
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
//返回一个先执行before函数对象apply方法再执行当前函数对象apply方法的函数对象
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}