【Java】函数式编程总结
函数式编程
概念:
使用代码以某种方式操纵其他代码,与传统的面向过程的编程以及面向对象的编程有所不太一样。函数式编程可以将方法作为参数,并使得调用者动态执行行为。这很大地提高了代码的可扩展性和维护性。让行为绑定更加灵活。
其中大部分用到了Lambda表达式和方法引用。有点类似与C++中的函数指针或是C#中的Delegate委托类。可以将函数方法作为一个变量传递给某个通用形式的接收器。之后用这个接收器去执行回调操作。
关于回调函数的定义:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
函数指针是C++的概念,在Java中可以理解为函数的引用。
函数式接口
语法规则: 有且仅有一个抽象方法的接口才能被视为函数式接口(Functional Interface)。
通常用
@FunctionalInterface
来进行与其它接口进行区分注释。
@FunctionalInterface
interface Callable{ void call();}
- 用函数式接口定义的变量来接收右侧的方法引用时,右侧的方法引用会隐式地转化为函数式接口的变量。
函数式接口变量 = 方法的引用 ( = 两侧性质其实不同,在Java的语法下会进行隐式转换)。
class Person{
void eat(){ ... };
}
class Test{
void test(){
Person p = new Person();
Callable c = p::eat;
c.eat(); //调用时,直接通过函数式接口中唯一的抽象方法来调用。
}
}
Lambda表达式
又叫匿名函数,Lambda表达式可以作为方法引用通过等号隐式转化为左侧的函数式接口变量。
interface Runnable{ void run();} //定义了一个只含一个函数的接口
class Test{
void test{
Runnable r; //用接口定义一个变量
r = () -> System.out.println("...") ; // 将Lambda表达式作为函数引用 赋值给 r
r.run(); //运行
}
}
Lambda表达式中会自行判断参数以及返回值类型
例子略。
方法引用
interface Callable{ void call(int i)}
class Person{
void walk(int step){...}
}
class Test{
void test{
Callbale c;
c = new Person()::walk;
}
}
如果是 普通方法 的引用,就用 对象名::普通方法名。
如果是 静态方法 的引用,就用 类名::静态方法名。
如果是 构造方法 的引用,就用 类名::new。
如果是 动态对象绑定 (原文是:非绑定接收)的引用, 还可以用 类名::普通方法名 的形式。这个时候需要在函数式接口的抽象方法上必须加一个参数,作为调用者传入。
class Person{
void eat(){ System.out.println("I'm eating"); }
}
interface Activity{
void doSomething(Person someone); //必须有个参数,用来接收调用者
}
class Test{
void test(){
Person person = new Person();
Activity act = Person::eat; //这个Person::eat 实际上是一个Lambda表达式的简易写法:
// (someone) -> { someone.eat(); }
act.doSomething(person);
}
}
用这种方式有个好处就是可以把调用者和行为分开来,动态调用的时候很方便。比如下面这个例子:
class Person{
String name;
Person(String name){ this.name = name;}
void eat(){ System.out.println(name+" is eating"); }
void sleep(){ System.out.println(name+" is sleeping"); }
void walk(){ System.out.println(name+" is walking"); }
void work(){ System.out.println(name+" is working");}
}
@FunctionalInterface
interface Activity{
void doSomething(Person someone);
}
public class FPTest1 {
public static void main(String[] args) {
Person[] player = {
new Person("Jimmy"),
new Person("Fish"),
new Person("Jack"),
new Person("Rose")
};
Activity[] act = {
Person::eat,
Person::sleep,
Person::walk,
Person::work,
};
Scanner sc = new Scanner(System.in);
int pi = sc.nextInt();
int ai = sc.nextInt();
SomeOneDoSomething( player[pi] , act[ai] ); //通过下标索引,动态改变"调用方"对应要做的"行为"。
}
static void SomeOneDoSomething(Person someone, Activity activity){
activity.doSomething(someone); //将调用方作为参数传入
}
}
常用内置接口
在java.util.function
包中有许多JDK提供的通用函数式接口。
其中主要为4种:
- Suppiler : 用于无输入地产生结果
- Consumer : 用于对输入进行执行或使用
- Predicate : 用于谓语逻辑的判断
- Function : 用于对输入加工处理后输出
1. Suppiler接口
首先是 Suppiler 的源码:
抽象方法: T get() 无参数,返回T类型
@FunctionalInterface
public interface Supplier<T> {
T get();
}
应用:
public class Test {
void test(){
Supplier<String> func0 = () -> "Hi!" + "Jimmy!"; //直接调用Supplier<T>
System.out.println( func0.get() );
}
}
2. Consumer接口
首先是 Consumer 的源码:
抽象方法: accept(T t) 参数类型T,无返回类型
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {...} //还有这个默认方法暂时这里不展开了
}
应用:
public class Test {
void test(){
Consumer<String> func1 = s -> System.out.println(s);
func1.accept("msg1");
}
}
3. Predicate接口
首先是 Predicate 的源码:
抽象方法: boolean test(T t) 接收T类型,返回boolean
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {...} //暂略
default Predicate<T> negate() {...} //暂略
// ... 还有好多暂略
}
应用:
public class Test {
void test(){
Predicate<Integer> isZero = num -> num==0? true:false;
System.out.println(isZero.test(1));
}
}
4. Function接口
首先是 Function 的源码:
抽象方法:R apply(T t); 接收T类型,返回R类型
public interface Function<T, R> {
R apply(T t);
//... 其余暂略
}
应用:
public class Test {
void test(){
Function<String,Integer> str2int = s -> Integer.parseInt(s);
int a = str2int.apply("10");
sout(a);
}
}