理解Java函数式编程

前言

不对概念进行理解,直接学应用,往往会不得要领,失去方向,所以学习前先弄明白概念是非常重要的。

参考:(英文还可以的,可以看看这个大神写的,比较全面,包含了函数式接口和Stream部分)
https://jenkov.com/tutorials/java-functional-programming/index.html

概念理解

在理解函数式编程之前,先回顾下什么是面向对象编程。所谓面向对象编程,简单一点理解:就是把现实中的事物抽象为一个对象,通过对象与对象之间的通信达到系统运作的目的。

而函数式编程,也可以理解为面向函数编程。那什么是面向函数编程呢,或者什么可以抽象为函数呢?其实就是过程,或者说是方法。(此处值得细品)这里不展开,下面通过Java内建的几个函数接口理解一下。

预备知识

  1. Java lambada表达式
  2. Java stream编程

Java内建的函数式接口

Function接口

接口定义:

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

抽象:这个接口完成了一件事情,传入一个类型T的参数,然后返回类型为R的结果。

看一个例子:

List<Student> studentList = 
        Arrays.asList(new Student("晓明",20),new Student("晓红",17));
Stream<Student> stream = studentList.stream();
List<String> nameList = stream.map(new Function<Student, String>() {
    @Override
    public String apply(Student student) {
        return student.getName();
    }
}).collect(Collectors.toList());

这里我们做了一件事情,将student的name取出来组成一个列表。当然上面是为了看清楚function,所以写成接口实现的形式,实际开发中我们常常使用lambada表达式:

stream.map(student->student.getName()).collect(Collectors.toList());
或者直接方法引用
stream.map(Student::getName).collect(Collectors.toList());

到这里,我们可以简单的理解,Function抽象了一种对象的transform。当然可能从很多角度可以有很多外延含义,大家可以自己发挥想象。

Predicate接口

接口定义:

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

抽象:传入一个参数,返回一个true/false

还是刚刚的例子,我要找出所有成年的学生:

List<Student> studentList =
		Arrays.asList(new Student("晓明",20),new Student("晓红",21));
Stream<Student> stream = studentList.stream();
List<Student> adultStudentList = stream.filter(new Predicate<Student>() {
        @Override
        public boolean test(Student student) {
		    return student.getAge() >= 18;
		}
}).collect(Collectors.toList());

lambada简化:
        
stream.filter(student->student.getAge() >= 18).collect(Collectors.toList());

我们可以简单的理解:Predicate抽象了【是】还是【不是】的这个逻辑。

UnaryOperator接口

接口定义:(unary:一元)

public interface UnaryOperator<T> extends Function<T, T> {
//这里忽略了一些静态方法
}

抽象:这里继承了Function<T,T>接口,通过泛型可以看到:传入类型T参数,返回还是T类型参数。

看个例子:过年了,所有的学生长一岁:

List<Student> studentList =
		Arrays.asList(new Student("晓明",20),new Student("晓红",21));
UnaryOperator<Student> ageIncrease = (student)-> {student.age = student.age + 1;return student;};
for (Student student : studentList) {
	ageIncrease.apply(student);
}	

这里就不举stream的例子了,stream里面的迭代器会用到UnaryOperator接口,感兴趣可以自己尝试用其实现一个斐波拉契数列?

BinaryOperator接口

接口定义:(unary:二元)

public interface BinaryOperator<T> extends BiFunction<T,T,T> {
    //省略静态方法
}

抽象:对比UnaryOperator接口(一元),BinaryOperator接口自然是二元(两个相同类型的运算参数)。

直接看例子:(算一下学生总年龄)

List<Student> studentList =
        Arrays.asList(new Student("晓明", 20),
                new Student("晓红", 21),
                new Student("张三", 22),
                new Student("李四", 23)
                );
Stream<Student> stream = studentList.stream();
Optional<Integer> reduce = stream.map(Student::getAge).reduce((age, totalAge) -> {
    return age + totalAge;
});
System.out.println(reduce.get());

这里大概写下reduce接口的运行过程:

  1. 第一次age=21,totalAge=22
  2. 第二次age=22,totalAge=43(21+22)
  3. 第三次age=23,totalAge=65(22+43)

Supplier接口

接口定义:

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

抽象:supplier英文意思是提供,这里可以理解为生成器?

还是看例子:(自动生成年龄)

Supplier<Integer> supplier = () -> new Integer((int) (Math.random() * 100D));

这个接口不常用,不再赘述,大家可以随意发挥。Stream.generate(Supplier)静态方法也可以用于生成斐波那契数列,可以自己试试。

Consumer接口

接口定义:(跟名字一样,消费者)

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

抽象:消费者…

看例子:过年了,所有的学生长一岁(上面写过了,用这个接口试一下)

List<Student> studentList =
				Arrays.asList(new Student("晓明", 20),new Student("晓红", 21));
Stream<Student> stream = studentList.stream();
//foreach的参数就是Consumer
stream.forEach(student -> student.age++);

结尾

剩下的就是大伙自己体会了,再结合Stream编程看看,应该会有所体悟。
当然Java还内置了很多函数式接口,想了解更多可以到java.util.function包下看看。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值