浅谈函数式编程

  1. 初识函数式接口

先介绍一下函数式接口,函数式接口就是有且只有一个抽象方法的接口(可以有多个非抽象方法),常见的函数式接口有Runnable,Comparable等

@FunctionalInterface
public interface Runnable {
    
    
    void run();
}

public interface Comparable<T> {
    
    
    int compareTo(T var1);
}

jdk1.8以后增加了java.util.function包下的常用函数式接口(Function、Consumer、Predicate、Supplier、BiFunction、IntBinaryOperator)以及它们的衍生函数式接口。

public interface Consumer<T> {
    
    
    void accept(T var1);
}

public interface Function<T, R> {
    
    
    R apply(T var1);
}

public interface Predicate<T> {
    
    
    boolean test(T var1);
}

public interface Supplier<T> {
    
    
    T get();
}

public interface BiFunction<T, U, R> {
    
    
    R apply(T var1, U var2);
}

public interface IntBinaryOperator {
    
    
    int applyAsInt(int var1, int var2);
}

上面的函数式接口都用了泛型,区别在于方法签名不同。因此,本文只使用前两个函数式接口来举例,其他的原理相似,首先以Function举例:

public class FunctionClient {
    
    
    String clientPrint() {
    
    
        return "this is functionClient test";
    }

    public static void main(String[] args) {
    
    
        FunctionInterface functionInterface = new FunctionInterface();
        //第一种写法
        System.out.println(functionInterface.functionTest(FunctionClient::clientPrint));
        //第二种写法
        System.out.println((String) functionInterface.functionTest(functionClient -> functionClient.clientPrint()));
    }
}

class FunctionInterface {
    
    
    FunctionClient functionClient = new FunctionClient();
    <R> R functionTest(Function<FunctionClient, R> function) {
    
    
        return function.apply(functionClient);
    }
}

这里用的函数式接口是Function,对应apply方法,FunctionalInterface类中的FunctionTest用来模拟sdk开发者给软件开发者的接口,该接口的入参是Function,出参是R:

<R> R functionTest(Function<FunctionClient, R> function) {
    
    
    return function.apply(functionClient);
}

在function对象调用apply方法时,实际上是执行了箭头后面的代码,也就是functionClient.clientPrint()。functionTest的入参就是表达式"functionClient -> functionClient.clientPrint())",箭头右侧执行的类型也就是函数式接口的返回值类型,由于functionClient.clientPrint()返回的是String,所以这里的R也特指String类型。

除了上面特指的FunctionClient对象,是否也可以使用普通类型来代替箭头左侧的FunctionClient类?

public class FunctionClient {
    
    
    String clientPrint() {
    
    
        return "this is functionClient test";
    }

    public static void main(String[] args) {
    
    
        FunctionInterface functionInterface = new FunctionInterface();
        Function<String, String> function = "This is left param." -> new FunctionClient().clientPrint();//报错行
        System.out.println(functionInterface.functionTest(function));
    }
}

class FunctionInterface {
    
    
    <R> R functionTest(Function<String, R> function) {
    
    
        return function.apply("This is FunctionalInterface");
    }
}

上面的代码会编译失败,原因在于Function无法接受String类型,以及其他常量。由于箭头左边代表的是一个方法的入参,那么这个入参对于该方法来说只能是变量,因此直接定义成常量会报错。

那么如何表示无参的场景嘞?只需要在箭头左边定义一个未声明的变量即可:

Function<String, String> function = s -> new FunctionClient().clientPrint();

对于函数式接口的赋值,箭头左侧只需要传入对应个数的非赋值的变量名即可,如果没有入参,直接使用()即可。

如果只有返回值,没有入参,可以使用Supplier接口:

public class FunctionClient {
    
    
    String clientPrint() {
    
    
        return "this is functionClient test";
    }

    public static void main(String[] args) {
    
    
        FunctionInterface functionInterface = new FunctionInterface();
        Supplier<String> function = () -> new FunctionClient().clientPrint();
        System.out.println(functionInterface.functionTest(function));
    }
}

class FunctionInterface {
    
    
    <R> R functionTest(Supplier<R> function) {
    
    
        return function.get();
    }
}
  1. 函数式编程的优点是什么?

简化代码,比如:

扫描二维码关注公众号,回复: 13014489 查看本文章
Runnable runnable1 = new Runnable() {
    
    
    @Override
    public void run() {
    
    
        System.out.println("Runnable");
    }
};

//第一次简化,函数式编程(语句lambda形式)
Runnable runnable2 = () -> {
    
    
    System.out.println("Runnable");
};

//第二次简化,函数式编程(表达式lambda形式)
Runnable runnable3 = () -> System.out.println("Runnable");

又比如:

Consumer<Integer> consumer = i -> System.out.println(i);
List<Integer> arrayList = new ArrayList<>(){
    
    
    {
    
    
        add(1);
        add(2);
        add(4);
    }
};
arrayList.forEach(consumer);
  1. lambda、函数式编程和回调的关系

1.lambda是函数式接口的实现,可以理解为语法糖。
2.回调对于接口中的方法数量没有限制,但是函数式接口只能指定一个抽象方法。
3.函数式编程和回调的方法定义都在调用方,两者的输入都是接口,只是其中一个是普通接口,另一个是函数式接口。
4.函数式编程和回调的方法都在被调用方执行,前者执行对应的唯一方法,比如apply,而后者可能对应多个方法,执行对应的方法即可。
5.两者在调用方都可以使用lambda的方式定义方法实现。
6.函数式编程实现方式是一种特殊的回调。

猜你喜欢

转载自blog.csdn.net/weixin_48968045/article/details/113148594