java之函数式编程

函数式编程和面向对象编程

百度百科上定义函数式编程:
函数式编程是种编程方式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。 [1]
和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。
和过程化编程相比,函数式编程里函数的计算可随时调用。
以此我们可以判断函数式编程和面向对象编程的区别是:面向对象编程参数接收的是对象,而函数式编程参数接受的是动作。刚开始接触java函数式编程可能不太好理解这个概念,那么我们就可以暂时理解为这个动作也是一个对象引用,类似与匿名对象。(这个是个坑。)

函数式接口

jdk8提供了大量的函数式接口,都在java.util.function包下。
定义
知识点一:该接口下有且仅有一个抽象方法。
知识点二:如果接口中声明了一个抽象方法,该抽象方法覆盖了Object中的public的方法,比如:

@FunctionalInterface
interface Test {
    void test();
    String toString();
}

虽然是两个抽象方法但是这个接口仍然可以看作为函数式接口。因为所有的对象的父类是Object因而所有的对象中默认会有Object中的方法。因此,此处的toString()只能看作是覆盖Object中的toString(),并不能作为影响函数接口有且仅有一个接口的定义,即接口数量并不会加1。
知识点三:函数式接口可以有一下三种方式来实现。
1、使用Lambda表达式。2、方法引用。3、构造器引用。

Lambda表达式

Lambda表达式的基本构成:
(item) -> { }
参数 + 箭头符号 + 执行体
使用Lambda表达式需要注意一下几点:
如果没有参数,那么“()”不能省略!
如果只有一个参数,那么“()” 可以省略!
如果有两个及以上参数,那么“()” 不能省略!

理解 :上面我们提到了Lambda只是函数式接口实现的一种方式,那么我们Lambda所实现的就是函数式接口中唯一的抽象方法,只是此时我们并没有关注函数式接口中抽象方法的名字!

接下来我们使用一段代码来诠释:
一、传统编程

List<String>  list = Arrays.asList("hello","world","bins");
        list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });

我们使用list的forEach进行内部迭代遍历list的内容并且输出list中的值。forEach(Consumer c)接收一个Consumer 对象,如果我们把Consumer 对象作为参数传给forEach方法,那么我们必须实现Consumer 中的方法,而Consumer 就是一个函数式接口,因此我们也可以使用Lambda表达式来实现!
二、Lambda方式

        List<String>  list = Arrays.asList("hello","world","bins");
        list.forEach(items -> System.out.println(items));

哇哦,代码瞬间清晰明朗很多,犹如雨后彩虹!在这个Lambda中并没有体现方法名字,但是它就是作为Consumer接口中accept的实现!

方法引用

方法引用的表示形式比较特殊,因此也容易记忆
方法引用一共可以分为4中:
1、类名::静态方法名。
2、引用名::实例方法名。
3、类名::实例方法名。(这种方式一般会接受两个参数,参数1会调用实例方法,参数2会作为实例方法的参数。)
4、类名::new。(这种方法也叫做构造器引用,也就是我们上文提到的实现函数式接口的三大形式。)

举个栗子

类名::静态方法名

		list.stream().map(Math::decrementExact).collect(Collectors.toList());

类名::实例方法名

 		List<String> list = Arrays.asList("hello ", "world", "bins9", "beijing");
        list.sort(String::compareToIgnoreCase);

引用名::实例方法名

		List<String>  list = Arrays.asList("hello","world","bins");
        list.forEach(System.out :: println);

类名::new

		Supplier<Student> supplier = Student::new;
        System.out.println(supplier.get().getName());

下面这个例子是使用自己创建的方法来实现 引用名::实例方法名

package com.bins9.test;

import java.util.function.BinaryOperator;

public class MyTest {

    public static void main(String[] args) {
        MyTest myTest = new MyTest();
        String test = myTest.test("bins","9",String::concat);
        System.out.println(test);
    }

    public String test(String s, String str, BinaryOperator<String> binaryOperator){
        return binaryOperator.apply(s,str);
    }

}
函数式接口

接下来我们来介绍一下java提供的函数式接口
1、Consumer

@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

Consumer,接受一个参数,没有返回值,类似于消费者。

2、BiConsumer<T, U>

@FunctionalInterface
public interface BiConsumer<T, U> {

    /**
     * Performs this operation on the given arguments.
     *
     * @param t the first input argument
     * @param u the second input argument
     */
    void accept(T t, U u);

BiConsumer<T, U>,接受两个参数,没有返回值,这个也是消费者。
3、Function<T, R>

@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

Function<T, R>,接受T类型的值,返回R类型的值。适合做适配器或者做映射。
4、BiFunction<T, U, R>

@FunctionalInterface
public interface BiFunction<T, U, R> {

    /**
     * Applies this function to the given arguments.
     *
     * @param t the first function argument
     * @param u the second function argument
     * @return the function result
     */
    R apply(T t, U u);

BiFunction<T, U, R>,接受两个参数t,u返回R类型的值。
5、BinaryOperator

@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T>

这个也是一个函数式接口,接受的值和返回的值是同一个类型,比较特殊。
6、Predicate

@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

Predicate ,类似与断言,接受一个参数,返回一个布尔值。
7、Supplier

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();

Supplier,不接受值,返回一个值,这个类似于生产者。
8、jdk8提供的函数式接口还有很多,上面罗列的是一些基本常用的,剩下的是一些变形,可以防止数据的拆箱和装箱可以提高运行性能,因此也非常重要,请自行查看!

比如:DoubleConsumer、IntConsumer等,这个就没有泛型了,因为值的类型已经确定了。

Lambda表达式和匿名方法的区别
    Runnable r1 = () -> System.out.println(this);

    Runnable r2 = new Runnable() {
        @Override
        public void run() {
            System.out.println(this);
        }
    };
    
    public static void main(String[] args) {
        
        LambdaTest lambdaTest = new LambdaTest();
        Thread t1 = new Thread(lambdaTest.r1);
        t1.start();
        System.out.println("--------");
        Thread t2 = new Thread(lambdaTest.r2);
        t2.start();

    }

这个例子,我们定义了两个Runnable。并且当它们执行的时候,我们让它输出this

--------
com.bins9.lambdaTest.LambdaTest@4295c176
com.bins9.lambdaTest.LambdaTest$1@6108a591

Process finished with exit code 0

这段是输出内容。
匿名类的输出方式是:所对应的对象是当前使用匿名内部类的对象 + $ + 数字 @ +hash值
数字代表这个匿名对象是当前对象的第几个匿名对象。

因此我们可以读懂上面这段输入内容,第一个this即Lambda表达式中的this表示的是当前对象。
而匿名对象中的this表示的是一个全新的作用域。这个就是Lambda和t匿名对象的最大区别!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值